]> Untitled Git - lemmy.git/blob - crates/db_queries/src/source/community.rs
Use URL type in most outstanding struct fields (#1468)
[lemmy.git] / crates / db_queries / src / source / community.rs
1 use crate::{ApubObject, Bannable, Crud, Followable, Joinable};
2 use diesel::{dsl::*, result::Error, *};
3 use lemmy_db_schema::{
4   naive_now,
5   source::community::{
6     Community,
7     CommunityFollower,
8     CommunityFollowerForm,
9     CommunityForm,
10     CommunityModerator,
11     CommunityModeratorForm,
12     CommunityUserBan,
13     CommunityUserBanForm,
14   },
15   DbUrl,
16 };
17
18 mod safe_type {
19   use crate::{source::community::Community, ToSafe};
20   use lemmy_db_schema::schema::community::*;
21
22   type Columns = (
23     id,
24     name,
25     title,
26     description,
27     creator_id,
28     removed,
29     published,
30     updated,
31     deleted,
32     nsfw,
33     actor_id,
34     local,
35     icon,
36     banner,
37   );
38
39   impl ToSafe for Community {
40     type SafeColumns = Columns;
41     fn safe_columns_tuple() -> Self::SafeColumns {
42       (
43         id,
44         name,
45         title,
46         description,
47         creator_id,
48         removed,
49         published,
50         updated,
51         deleted,
52         nsfw,
53         actor_id,
54         local,
55         icon,
56         banner,
57       )
58     }
59   }
60 }
61
62 impl Crud<CommunityForm> for Community {
63   fn read(conn: &PgConnection, community_id: i32) -> Result<Self, Error> {
64     use lemmy_db_schema::schema::community::dsl::*;
65     community.find(community_id).first::<Self>(conn)
66   }
67
68   fn delete(conn: &PgConnection, community_id: i32) -> Result<usize, Error> {
69     use lemmy_db_schema::schema::community::dsl::*;
70     diesel::delete(community.find(community_id)).execute(conn)
71   }
72
73   fn create(conn: &PgConnection, new_community: &CommunityForm) -> Result<Self, Error> {
74     use lemmy_db_schema::schema::community::dsl::*;
75     insert_into(community)
76       .values(new_community)
77       .get_result::<Self>(conn)
78   }
79
80   fn update(
81     conn: &PgConnection,
82     community_id: i32,
83     new_community: &CommunityForm,
84   ) -> Result<Self, Error> {
85     use lemmy_db_schema::schema::community::dsl::*;
86     diesel::update(community.find(community_id))
87       .set(new_community)
88       .get_result::<Self>(conn)
89   }
90 }
91
92 impl ApubObject<CommunityForm> for Community {
93   fn read_from_apub_id(conn: &PgConnection, for_actor_id: &DbUrl) -> Result<Self, Error> {
94     use lemmy_db_schema::schema::community::dsl::*;
95     community
96       .filter(actor_id.eq(for_actor_id))
97       .first::<Self>(conn)
98   }
99
100   fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
101     use lemmy_db_schema::schema::community::dsl::*;
102     insert_into(community)
103       .values(community_form)
104       .on_conflict(actor_id)
105       .do_update()
106       .set(community_form)
107       .get_result::<Self>(conn)
108   }
109 }
110
111 pub trait Community_ {
112   fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error>;
113   fn update_deleted(
114     conn: &PgConnection,
115     community_id: i32,
116     new_deleted: bool,
117   ) -> Result<Community, Error>;
118   fn update_removed(
119     conn: &PgConnection,
120     community_id: i32,
121     new_removed: bool,
122   ) -> Result<Community, Error>;
123   fn update_removed_for_creator(
124     conn: &PgConnection,
125     for_creator_id: i32,
126     new_removed: bool,
127   ) -> Result<Vec<Community>, Error>;
128   fn update_creator(
129     conn: &PgConnection,
130     community_id: i32,
131     new_creator_id: i32,
132   ) -> Result<Community, Error>;
133   fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>;
134   fn read_from_followers_url(
135     conn: &PgConnection,
136     followers_url: &DbUrl,
137   ) -> Result<Community, Error>;
138 }
139
140 impl Community_ for Community {
141   fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error> {
142     use lemmy_db_schema::schema::community::dsl::*;
143     community
144       .filter(local.eq(true))
145       .filter(name.eq(community_name))
146       .first::<Self>(conn)
147   }
148
149   fn update_deleted(
150     conn: &PgConnection,
151     community_id: i32,
152     new_deleted: bool,
153   ) -> Result<Community, Error> {
154     use lemmy_db_schema::schema::community::dsl::*;
155     diesel::update(community.find(community_id))
156       .set((deleted.eq(new_deleted), updated.eq(naive_now())))
157       .get_result::<Self>(conn)
158   }
159
160   fn update_removed(
161     conn: &PgConnection,
162     community_id: i32,
163     new_removed: bool,
164   ) -> Result<Community, Error> {
165     use lemmy_db_schema::schema::community::dsl::*;
166     diesel::update(community.find(community_id))
167       .set((removed.eq(new_removed), updated.eq(naive_now())))
168       .get_result::<Self>(conn)
169   }
170
171   fn update_removed_for_creator(
172     conn: &PgConnection,
173     for_creator_id: i32,
174     new_removed: bool,
175   ) -> Result<Vec<Community>, Error> {
176     use lemmy_db_schema::schema::community::dsl::*;
177     diesel::update(community.filter(creator_id.eq(for_creator_id)))
178       .set((removed.eq(new_removed), updated.eq(naive_now())))
179       .get_results::<Self>(conn)
180   }
181
182   fn update_creator(
183     conn: &PgConnection,
184     community_id: i32,
185     new_creator_id: i32,
186   ) -> Result<Community, Error> {
187     use lemmy_db_schema::schema::community::dsl::*;
188     diesel::update(community.find(community_id))
189       .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
190       .get_result::<Self>(conn)
191   }
192
193   fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error> {
194     use lemmy_db_schema::schema::community::dsl::*;
195     community.select(actor_id).distinct().load::<String>(conn)
196   }
197
198   fn read_from_followers_url(
199     conn: &PgConnection,
200     followers_url_: &DbUrl,
201   ) -> Result<Community, Error> {
202     use lemmy_db_schema::schema::community::dsl::*;
203     community
204       .filter(followers_url.eq(followers_url_))
205       .first::<Self>(conn)
206   }
207 }
208
209 impl Joinable<CommunityModeratorForm> for CommunityModerator {
210   fn join(
211     conn: &PgConnection,
212     community_user_form: &CommunityModeratorForm,
213   ) -> Result<Self, Error> {
214     use lemmy_db_schema::schema::community_moderator::dsl::*;
215     insert_into(community_moderator)
216       .values(community_user_form)
217       .get_result::<Self>(conn)
218   }
219
220   fn leave(
221     conn: &PgConnection,
222     community_user_form: &CommunityModeratorForm,
223   ) -> Result<usize, Error> {
224     use lemmy_db_schema::schema::community_moderator::dsl::*;
225     diesel::delete(
226       community_moderator
227         .filter(community_id.eq(community_user_form.community_id))
228         .filter(user_id.eq(community_user_form.user_id)),
229     )
230     .execute(conn)
231   }
232 }
233
234 pub trait CommunityModerator_ {
235   fn delete_for_community(conn: &PgConnection, for_community_id: i32) -> Result<usize, Error>;
236   fn get_user_moderated_communities(
237     conn: &PgConnection,
238     for_user_id: i32,
239   ) -> Result<Vec<i32>, Error>;
240 }
241
242 impl CommunityModerator_ for CommunityModerator {
243   fn delete_for_community(conn: &PgConnection, for_community_id: i32) -> Result<usize, Error> {
244     use lemmy_db_schema::schema::community_moderator::dsl::*;
245     diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
246   }
247
248   fn get_user_moderated_communities(
249     conn: &PgConnection,
250     for_user_id: i32,
251   ) -> Result<Vec<i32>, Error> {
252     use lemmy_db_schema::schema::community_moderator::dsl::*;
253     community_moderator
254       .filter(user_id.eq(for_user_id))
255       .select(community_id)
256       .load::<i32>(conn)
257   }
258 }
259
260 impl Bannable<CommunityUserBanForm> for CommunityUserBan {
261   fn ban(
262     conn: &PgConnection,
263     community_user_ban_form: &CommunityUserBanForm,
264   ) -> Result<Self, Error> {
265     use lemmy_db_schema::schema::community_user_ban::dsl::*;
266     insert_into(community_user_ban)
267       .values(community_user_ban_form)
268       .get_result::<Self>(conn)
269   }
270
271   fn unban(
272     conn: &PgConnection,
273     community_user_ban_form: &CommunityUserBanForm,
274   ) -> Result<usize, Error> {
275     use lemmy_db_schema::schema::community_user_ban::dsl::*;
276     diesel::delete(
277       community_user_ban
278         .filter(community_id.eq(community_user_ban_form.community_id))
279         .filter(user_id.eq(community_user_ban_form.user_id)),
280     )
281     .execute(conn)
282   }
283 }
284
285 impl Followable<CommunityFollowerForm> for CommunityFollower {
286   fn follow(
287     conn: &PgConnection,
288     community_follower_form: &CommunityFollowerForm,
289   ) -> Result<Self, Error> {
290     use lemmy_db_schema::schema::community_follower::dsl::*;
291     insert_into(community_follower)
292       .values(community_follower_form)
293       .on_conflict((community_id, user_id))
294       .do_update()
295       .set(community_follower_form)
296       .get_result::<Self>(conn)
297   }
298   fn follow_accepted(conn: &PgConnection, community_id_: i32, user_id_: i32) -> Result<Self, Error>
299   where
300     Self: Sized,
301   {
302     use lemmy_db_schema::schema::community_follower::dsl::*;
303     diesel::update(
304       community_follower
305         .filter(community_id.eq(community_id_))
306         .filter(user_id.eq(user_id_)),
307     )
308     .set(pending.eq(true))
309     .get_result::<Self>(conn)
310   }
311   fn unfollow(
312     conn: &PgConnection,
313     community_follower_form: &CommunityFollowerForm,
314   ) -> Result<usize, Error> {
315     use lemmy_db_schema::schema::community_follower::dsl::*;
316     diesel::delete(
317       community_follower
318         .filter(community_id.eq(&community_follower_form.community_id))
319         .filter(user_id.eq(&community_follower_form.user_id)),
320     )
321     .execute(conn)
322   }
323   // TODO: this function name only makes sense if you call it with a remote community. for a local
324   //       community, it will also return true if only remote followers exist
325   fn has_local_followers(conn: &PgConnection, community_id_: i32) -> Result<bool, Error> {
326     use lemmy_db_schema::schema::community_follower::dsl::*;
327     diesel::select(exists(
328       community_follower.filter(community_id.eq(community_id_)),
329     ))
330     .get_result(conn)
331   }
332 }
333
334 #[cfg(test)]
335 mod tests {
336   use crate::{
337     establish_unpooled_connection,
338     Bannable,
339     Crud,
340     Followable,
341     Joinable,
342     ListingType,
343     SortType,
344   };
345   use lemmy_db_schema::source::{community::*, user::*};
346   use serial_test::serial;
347
348   #[test]
349   #[serial]
350   fn test_crud() {
351     let conn = establish_unpooled_connection();
352
353     let new_user = UserForm {
354       name: "bobbee".into(),
355       preferred_username: None,
356       password_encrypted: "nope".into(),
357       email: None,
358       matrix_user_id: None,
359       avatar: None,
360       banner: None,
361       admin: false,
362       banned: Some(false),
363       published: None,
364       updated: None,
365       show_nsfw: false,
366       theme: "browser".into(),
367       default_sort_type: SortType::Hot as i16,
368       default_listing_type: ListingType::Subscribed as i16,
369       lang: "browser".into(),
370       show_avatars: true,
371       send_notifications_to_email: false,
372       actor_id: None,
373       bio: None,
374       local: true,
375       private_key: None,
376       public_key: None,
377       last_refreshed_at: None,
378       inbox_url: None,
379       shared_inbox_url: None,
380     };
381
382     let inserted_user = User_::create(&conn, &new_user).unwrap();
383
384     let new_community = CommunityForm {
385       name: "TIL".into(),
386       creator_id: inserted_user.id,
387       title: "nada".to_owned(),
388       description: None,
389       nsfw: false,
390       removed: None,
391       deleted: None,
392       updated: None,
393       actor_id: None,
394       local: true,
395       private_key: None,
396       public_key: None,
397       last_refreshed_at: None,
398       published: None,
399       icon: None,
400       banner: None,
401       followers_url: None,
402       inbox_url: None,
403       shared_inbox_url: None,
404     };
405
406     let inserted_community = Community::create(&conn, &new_community).unwrap();
407
408     let expected_community = Community {
409       id: inserted_community.id,
410       creator_id: inserted_user.id,
411       name: "TIL".into(),
412       title: "nada".to_owned(),
413       description: None,
414       nsfw: false,
415       removed: false,
416       deleted: false,
417       published: inserted_community.published,
418       updated: None,
419       actor_id: inserted_community.actor_id.to_owned(),
420       local: true,
421       private_key: None,
422       public_key: None,
423       last_refreshed_at: inserted_community.published,
424       icon: None,
425       banner: None,
426       followers_url: inserted_community.followers_url.to_owned(),
427       inbox_url: inserted_community.inbox_url.to_owned(),
428       shared_inbox_url: None,
429     };
430
431     let community_follower_form = CommunityFollowerForm {
432       community_id: inserted_community.id,
433       user_id: inserted_user.id,
434       pending: false,
435     };
436
437     let inserted_community_follower =
438       CommunityFollower::follow(&conn, &community_follower_form).unwrap();
439
440     let expected_community_follower = CommunityFollower {
441       id: inserted_community_follower.id,
442       community_id: inserted_community.id,
443       user_id: inserted_user.id,
444       pending: Some(false),
445       published: inserted_community_follower.published,
446     };
447
448     let community_user_form = CommunityModeratorForm {
449       community_id: inserted_community.id,
450       user_id: inserted_user.id,
451     };
452
453     let inserted_community_user = CommunityModerator::join(&conn, &community_user_form).unwrap();
454
455     let expected_community_user = CommunityModerator {
456       id: inserted_community_user.id,
457       community_id: inserted_community.id,
458       user_id: inserted_user.id,
459       published: inserted_community_user.published,
460     };
461
462     let community_user_ban_form = CommunityUserBanForm {
463       community_id: inserted_community.id,
464       user_id: inserted_user.id,
465     };
466
467     let inserted_community_user_ban =
468       CommunityUserBan::ban(&conn, &community_user_ban_form).unwrap();
469
470     let expected_community_user_ban = CommunityUserBan {
471       id: inserted_community_user_ban.id,
472       community_id: inserted_community.id,
473       user_id: inserted_user.id,
474       published: inserted_community_user_ban.published,
475     };
476
477     let read_community = Community::read(&conn, inserted_community.id).unwrap();
478     let updated_community =
479       Community::update(&conn, inserted_community.id, &new_community).unwrap();
480     let ignored_community = CommunityFollower::unfollow(&conn, &community_follower_form).unwrap();
481     let left_community = CommunityModerator::leave(&conn, &community_user_form).unwrap();
482     let unban = CommunityUserBan::unban(&conn, &community_user_ban_form).unwrap();
483     let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
484     User_::delete(&conn, inserted_user.id).unwrap();
485
486     assert_eq!(expected_community, read_community);
487     assert_eq!(expected_community, inserted_community);
488     assert_eq!(expected_community, updated_community);
489     assert_eq!(expected_community_follower, inserted_community_follower);
490     assert_eq!(expected_community_user, inserted_community_user);
491     assert_eq!(expected_community_user_ban, inserted_community_user_ban);
492     assert_eq!(1, ignored_community);
493     assert_eq!(1, left_community);
494     assert_eq!(1, unban);
495     // assert_eq!(2, loaded_count);
496     assert_eq!(1, num_deleted);
497   }
498 }