]> Untitled Git - lemmy.git/blob - crates/db_views/src/registration_application_view.rs
Add infinite scroll user option (#3572)
[lemmy.git] / crates / db_views / src / registration_application_view.rs
1 use crate::structs::RegistrationApplicationView;
2 use diesel::{
3   dsl::count,
4   result::Error,
5   ExpressionMethods,
6   JoinOnDsl,
7   NullableExpressionMethods,
8   QueryDsl,
9 };
10 use diesel_async::RunQueryDsl;
11 use lemmy_db_schema::{
12   schema::{local_user, person, registration_application},
13   source::{
14     local_user::LocalUser,
15     person::Person,
16     registration_application::RegistrationApplication,
17   },
18   traits::JoinView,
19   utils::{get_conn, limit_and_offset, DbPool},
20 };
21 use typed_builder::TypedBuilder;
22
23 type RegistrationApplicationViewTuple =
24   (RegistrationApplication, LocalUser, Person, Option<Person>);
25
26 impl RegistrationApplicationView {
27   pub async fn read(
28     pool: &mut DbPool<'_>,
29     registration_application_id: i32,
30   ) -> Result<Self, Error> {
31     let conn = &mut get_conn(pool).await?;
32     let person_alias_1 = diesel::alias!(person as person1);
33
34     let (registration_application, creator_local_user, creator, admin) =
35       registration_application::table
36         .find(registration_application_id)
37         .inner_join(
38           local_user::table.on(registration_application::local_user_id.eq(local_user::id)),
39         )
40         .inner_join(person::table.on(local_user::person_id.eq(person::id)))
41         .left_join(
42           person_alias_1
43             .on(registration_application::admin_id.eq(person_alias_1.field(person::id).nullable())),
44         )
45         .order_by(registration_application::published.desc())
46         .select((
47           registration_application::all_columns,
48           local_user::all_columns,
49           person::all_columns,
50           person_alias_1.fields(person::all_columns).nullable(),
51         ))
52         .first::<RegistrationApplicationViewTuple>(conn)
53         .await?;
54
55     Ok(RegistrationApplicationView {
56       registration_application,
57       creator_local_user,
58       creator,
59       admin,
60     })
61   }
62
63   /// Returns the current unread registration_application count
64   pub async fn get_unread_count(
65     pool: &mut DbPool<'_>,
66     verified_email_only: bool,
67   ) -> Result<i64, Error> {
68     let conn = &mut get_conn(pool).await?;
69     let person_alias_1 = diesel::alias!(person as person1);
70
71     let mut query = registration_application::table
72       .inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
73       .inner_join(person::table.on(local_user::person_id.eq(person::id)))
74       .left_join(
75         person_alias_1
76           .on(registration_application::admin_id.eq(person_alias_1.field(person::id).nullable())),
77       )
78       .filter(registration_application::admin_id.is_null())
79       .into_boxed();
80
81     if verified_email_only {
82       query = query.filter(local_user::email_verified.eq(true))
83     }
84
85     query
86       .select(count(registration_application::id))
87       .first::<i64>(conn)
88       .await
89   }
90 }
91
92 #[derive(TypedBuilder)]
93 #[builder(field_defaults(default))]
94 pub struct RegistrationApplicationQuery<'a, 'b: 'a> {
95   #[builder(!default)]
96   pool: &'a mut DbPool<'b>,
97   unread_only: Option<bool>,
98   verified_email_only: Option<bool>,
99   page: Option<i64>,
100   limit: Option<i64>,
101 }
102
103 impl<'a, 'b: 'a> RegistrationApplicationQuery<'a, 'b> {
104   pub async fn list(self) -> Result<Vec<RegistrationApplicationView>, Error> {
105     let conn = &mut get_conn(self.pool).await?;
106     let person_alias_1 = diesel::alias!(person as person1);
107
108     let mut query = registration_application::table
109       .inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
110       .inner_join(person::table.on(local_user::person_id.eq(person::id)))
111       .left_join(
112         person_alias_1
113           .on(registration_application::admin_id.eq(person_alias_1.field(person::id).nullable())),
114       )
115       .order_by(registration_application::published.desc())
116       .select((
117         registration_application::all_columns,
118         local_user::all_columns,
119         person::all_columns,
120         person_alias_1.fields(person::all_columns).nullable(),
121       ))
122       .into_boxed();
123
124     if self.unread_only.unwrap_or(false) {
125       query = query.filter(registration_application::admin_id.is_null())
126     }
127
128     if self.verified_email_only.unwrap_or(false) {
129       query = query.filter(local_user::email_verified.eq(true))
130     }
131
132     let (limit, offset) = limit_and_offset(self.page, self.limit)?;
133
134     query = query
135       .limit(limit)
136       .offset(offset)
137       .order_by(registration_application::published.desc());
138
139     let res = query.load::<RegistrationApplicationViewTuple>(conn).await?;
140
141     Ok(
142       res
143         .into_iter()
144         .map(RegistrationApplicationView::from_tuple)
145         .collect(),
146     )
147   }
148 }
149
150 impl JoinView for RegistrationApplicationView {
151   type JoinTuple = RegistrationApplicationViewTuple;
152   fn from_tuple(a: Self::JoinTuple) -> Self {
153     Self {
154       registration_application: a.0,
155       creator_local_user: a.1,
156       creator: a.2,
157       admin: a.3,
158     }
159   }
160 }
161
162 #[cfg(test)]
163 mod tests {
164   use crate::registration_application_view::{
165     RegistrationApplicationQuery,
166     RegistrationApplicationView,
167   };
168   use lemmy_db_schema::{
169     source::{
170       instance::Instance,
171       local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
172       person::{Person, PersonInsertForm},
173       registration_application::{
174         RegistrationApplication,
175         RegistrationApplicationInsertForm,
176         RegistrationApplicationUpdateForm,
177       },
178     },
179     traits::Crud,
180     utils::build_db_pool_for_tests,
181   };
182   use serial_test::serial;
183
184   #[tokio::test]
185   #[serial]
186   async fn test_crud() {
187     let pool = &build_db_pool_for_tests().await;
188     let pool = &mut pool.into();
189
190     let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
191       .await
192       .unwrap();
193
194     let timmy_person_form = PersonInsertForm::builder()
195       .name("timmy_rav".into())
196       .admin(Some(true))
197       .public_key("pubkey".to_string())
198       .instance_id(inserted_instance.id)
199       .build();
200
201     let inserted_timmy_person = Person::create(pool, &timmy_person_form).await.unwrap();
202
203     let timmy_local_user_form = LocalUserInsertForm::builder()
204       .person_id(inserted_timmy_person.id)
205       .password_encrypted("nada".to_string())
206       .build();
207
208     let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form)
209       .await
210       .unwrap();
211
212     let sara_person_form = PersonInsertForm::builder()
213       .name("sara_rav".into())
214       .public_key("pubkey".to_string())
215       .instance_id(inserted_instance.id)
216       .build();
217
218     let inserted_sara_person = Person::create(pool, &sara_person_form).await.unwrap();
219
220     let sara_local_user_form = LocalUserInsertForm::builder()
221       .person_id(inserted_sara_person.id)
222       .password_encrypted("nada".to_string())
223       .build();
224
225     let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form)
226       .await
227       .unwrap();
228
229     // Sara creates an application
230     let sara_app_form = RegistrationApplicationInsertForm {
231       local_user_id: inserted_sara_local_user.id,
232       answer: "LET ME IIIIINN".to_string(),
233     };
234
235     let sara_app = RegistrationApplication::create(pool, &sara_app_form)
236       .await
237       .unwrap();
238
239     let read_sara_app_view = RegistrationApplicationView::read(pool, sara_app.id)
240       .await
241       .unwrap();
242
243     let jess_person_form = PersonInsertForm::builder()
244       .name("jess_rav".into())
245       .public_key("pubkey".to_string())
246       .instance_id(inserted_instance.id)
247       .build();
248
249     let inserted_jess_person = Person::create(pool, &jess_person_form).await.unwrap();
250
251     let jess_local_user_form = LocalUserInsertForm::builder()
252       .person_id(inserted_jess_person.id)
253       .password_encrypted("nada".to_string())
254       .build();
255
256     let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form)
257       .await
258       .unwrap();
259
260     // Sara creates an application
261     let jess_app_form = RegistrationApplicationInsertForm {
262       local_user_id: inserted_jess_local_user.id,
263       answer: "LET ME IIIIINN".to_string(),
264     };
265
266     let jess_app = RegistrationApplication::create(pool, &jess_app_form)
267       .await
268       .unwrap();
269
270     let read_jess_app_view = RegistrationApplicationView::read(pool, jess_app.id)
271       .await
272       .unwrap();
273
274     let mut expected_sara_app_view = RegistrationApplicationView {
275       registration_application: sara_app.clone(),
276       creator_local_user: LocalUser {
277         id: inserted_sara_local_user.id,
278         person_id: inserted_sara_local_user.person_id,
279         email: inserted_sara_local_user.email,
280         show_nsfw: inserted_sara_local_user.show_nsfw,
281         theme: inserted_sara_local_user.theme,
282         default_sort_type: inserted_sara_local_user.default_sort_type,
283         default_listing_type: inserted_sara_local_user.default_listing_type,
284         interface_language: inserted_sara_local_user.interface_language,
285         show_avatars: inserted_sara_local_user.show_avatars,
286         send_notifications_to_email: inserted_sara_local_user.send_notifications_to_email,
287         validator_time: inserted_sara_local_user.validator_time,
288         show_bot_accounts: inserted_sara_local_user.show_bot_accounts,
289         show_scores: inserted_sara_local_user.show_scores,
290         show_read_posts: inserted_sara_local_user.show_read_posts,
291         show_new_post_notifs: inserted_sara_local_user.show_new_post_notifs,
292         email_verified: inserted_sara_local_user.email_verified,
293         accepted_application: inserted_sara_local_user.accepted_application,
294         totp_2fa_secret: inserted_sara_local_user.totp_2fa_secret,
295         totp_2fa_url: inserted_sara_local_user.totp_2fa_url,
296         password_encrypted: inserted_sara_local_user.password_encrypted,
297         open_links_in_new_tab: inserted_sara_local_user.open_links_in_new_tab,
298         infinite_scroll_enabled: inserted_sara_local_user.infinite_scroll_enabled,
299       },
300       creator: Person {
301         id: inserted_sara_person.id,
302         name: inserted_sara_person.name.clone(),
303         display_name: None,
304         published: inserted_sara_person.published,
305         avatar: None,
306         actor_id: inserted_sara_person.actor_id.clone(),
307         local: true,
308         banned: false,
309         ban_expires: None,
310         deleted: false,
311         admin: false,
312         bot_account: false,
313         bio: None,
314         banner: None,
315         updated: None,
316         inbox_url: inserted_sara_person.inbox_url.clone(),
317         shared_inbox_url: None,
318         matrix_user_id: None,
319         instance_id: inserted_instance.id,
320         private_key: inserted_sara_person.private_key,
321         public_key: inserted_sara_person.public_key,
322         last_refreshed_at: inserted_sara_person.last_refreshed_at,
323       },
324       admin: None,
325     };
326
327     assert_eq!(read_sara_app_view, expected_sara_app_view);
328
329     // Do a batch read of the applications
330     let apps = RegistrationApplicationQuery::builder()
331       .pool(pool)
332       .unread_only(Some(true))
333       .build()
334       .list()
335       .await
336       .unwrap();
337
338     assert_eq!(
339       apps,
340       [read_jess_app_view.clone(), expected_sara_app_view.clone()]
341     );
342
343     // Make sure the counts are correct
344     let unread_count = RegistrationApplicationView::get_unread_count(pool, false)
345       .await
346       .unwrap();
347     assert_eq!(unread_count, 2);
348
349     // Approve the application
350     let approve_form = RegistrationApplicationUpdateForm {
351       admin_id: Some(Some(inserted_timmy_person.id)),
352       deny_reason: None,
353     };
354
355     RegistrationApplication::update(pool, sara_app.id, &approve_form)
356       .await
357       .unwrap();
358
359     // Update the local_user row
360     let approve_local_user_form = LocalUserUpdateForm::builder()
361       .accepted_application(Some(true))
362       .build();
363
364     LocalUser::update(pool, inserted_sara_local_user.id, &approve_local_user_form)
365       .await
366       .unwrap();
367
368     let read_sara_app_view_after_approve = RegistrationApplicationView::read(pool, sara_app.id)
369       .await
370       .unwrap();
371
372     // Make sure the columns changed
373     expected_sara_app_view
374       .creator_local_user
375       .accepted_application = true;
376     expected_sara_app_view.registration_application.admin_id = Some(inserted_timmy_person.id);
377
378     expected_sara_app_view.admin = Some(Person {
379       id: inserted_timmy_person.id,
380       name: inserted_timmy_person.name.clone(),
381       display_name: None,
382       published: inserted_timmy_person.published,
383       avatar: None,
384       actor_id: inserted_timmy_person.actor_id.clone(),
385       local: true,
386       banned: false,
387       ban_expires: None,
388       deleted: false,
389       admin: true,
390       bot_account: false,
391       bio: None,
392       banner: None,
393       updated: None,
394       inbox_url: inserted_timmy_person.inbox_url.clone(),
395       shared_inbox_url: None,
396       matrix_user_id: None,
397       instance_id: inserted_instance.id,
398       private_key: inserted_timmy_person.private_key,
399       public_key: inserted_timmy_person.public_key,
400       last_refreshed_at: inserted_timmy_person.last_refreshed_at,
401     });
402     assert_eq!(read_sara_app_view_after_approve, expected_sara_app_view);
403
404     // Do a batch read of apps again
405     // It should show only jessicas which is unresolved
406     let apps_after_resolve = RegistrationApplicationQuery::builder()
407       .pool(pool)
408       .unread_only(Some(true))
409       .build()
410       .list()
411       .await
412       .unwrap();
413     assert_eq!(apps_after_resolve, vec![read_jess_app_view]);
414
415     // Make sure the counts are correct
416     let unread_count_after_approve = RegistrationApplicationView::get_unread_count(pool, false)
417       .await
418       .unwrap();
419     assert_eq!(unread_count_after_approve, 1);
420
421     // Make sure the not undenied_only has all the apps
422     let all_apps = RegistrationApplicationQuery::builder()
423       .pool(pool)
424       .build()
425       .list()
426       .await
427       .unwrap();
428     assert_eq!(all_apps.len(), 2);
429
430     Person::delete(pool, inserted_timmy_person.id)
431       .await
432       .unwrap();
433     Person::delete(pool, inserted_sara_person.id).await.unwrap();
434     Person::delete(pool, inserted_jess_person.id).await.unwrap();
435     Instance::delete(pool, inserted_instance.id).await.unwrap();
436   }
437 }