]> Untitled Git - lemmy.git/blob - crates/db_queries/src/source/community.rs
08421bd5d67d3103b77af19687af01e858a66f68
[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   Url,
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: &Url) -> 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(conn: &PgConnection, followers_url: &Url) -> Result<Community, Error>;
135 }
136
137 impl Community_ for Community {
138   fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error> {
139     use lemmy_db_schema::schema::community::dsl::*;
140     community
141       .filter(local.eq(true))
142       .filter(name.eq(community_name))
143       .first::<Self>(conn)
144   }
145
146   fn update_deleted(
147     conn: &PgConnection,
148     community_id: i32,
149     new_deleted: bool,
150   ) -> Result<Community, Error> {
151     use lemmy_db_schema::schema::community::dsl::*;
152     diesel::update(community.find(community_id))
153       .set((deleted.eq(new_deleted), updated.eq(naive_now())))
154       .get_result::<Self>(conn)
155   }
156
157   fn update_removed(
158     conn: &PgConnection,
159     community_id: i32,
160     new_removed: bool,
161   ) -> Result<Community, Error> {
162     use lemmy_db_schema::schema::community::dsl::*;
163     diesel::update(community.find(community_id))
164       .set((removed.eq(new_removed), updated.eq(naive_now())))
165       .get_result::<Self>(conn)
166   }
167
168   fn update_removed_for_creator(
169     conn: &PgConnection,
170     for_creator_id: i32,
171     new_removed: bool,
172   ) -> Result<Vec<Community>, Error> {
173     use lemmy_db_schema::schema::community::dsl::*;
174     diesel::update(community.filter(creator_id.eq(for_creator_id)))
175       .set((removed.eq(new_removed), updated.eq(naive_now())))
176       .get_results::<Self>(conn)
177   }
178
179   fn update_creator(
180     conn: &PgConnection,
181     community_id: i32,
182     new_creator_id: i32,
183   ) -> Result<Community, Error> {
184     use lemmy_db_schema::schema::community::dsl::*;
185     diesel::update(community.find(community_id))
186       .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
187       .get_result::<Self>(conn)
188   }
189
190   fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error> {
191     use lemmy_db_schema::schema::community::dsl::*;
192     community.select(actor_id).distinct().load::<String>(conn)
193   }
194
195   fn read_from_followers_url(
196     conn: &PgConnection,
197     followers_url_: &Url,
198   ) -> Result<Community, Error> {
199     use lemmy_db_schema::schema::community::dsl::*;
200     community
201       .filter(followers_url.eq(followers_url_))
202       .first::<Self>(conn)
203   }
204 }
205
206 impl Joinable<CommunityModeratorForm> for CommunityModerator {
207   fn join(
208     conn: &PgConnection,
209     community_user_form: &CommunityModeratorForm,
210   ) -> Result<Self, Error> {
211     use lemmy_db_schema::schema::community_moderator::dsl::*;
212     insert_into(community_moderator)
213       .values(community_user_form)
214       .get_result::<Self>(conn)
215   }
216
217   fn leave(
218     conn: &PgConnection,
219     community_user_form: &CommunityModeratorForm,
220   ) -> Result<usize, Error> {
221     use lemmy_db_schema::schema::community_moderator::dsl::*;
222     diesel::delete(
223       community_moderator
224         .filter(community_id.eq(community_user_form.community_id))
225         .filter(user_id.eq(community_user_form.user_id)),
226     )
227     .execute(conn)
228   }
229 }
230
231 pub trait CommunityModerator_ {
232   fn delete_for_community(conn: &PgConnection, for_community_id: i32) -> Result<usize, Error>;
233   fn get_user_moderated_communities(
234     conn: &PgConnection,
235     for_user_id: i32,
236   ) -> Result<Vec<i32>, Error>;
237 }
238
239 impl CommunityModerator_ for CommunityModerator {
240   fn delete_for_community(conn: &PgConnection, for_community_id: i32) -> Result<usize, Error> {
241     use lemmy_db_schema::schema::community_moderator::dsl::*;
242     diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
243   }
244
245   fn get_user_moderated_communities(
246     conn: &PgConnection,
247     for_user_id: i32,
248   ) -> Result<Vec<i32>, Error> {
249     use lemmy_db_schema::schema::community_moderator::dsl::*;
250     community_moderator
251       .filter(user_id.eq(for_user_id))
252       .select(community_id)
253       .load::<i32>(conn)
254   }
255 }
256
257 impl Bannable<CommunityUserBanForm> for CommunityUserBan {
258   fn ban(
259     conn: &PgConnection,
260     community_user_ban_form: &CommunityUserBanForm,
261   ) -> Result<Self, Error> {
262     use lemmy_db_schema::schema::community_user_ban::dsl::*;
263     insert_into(community_user_ban)
264       .values(community_user_ban_form)
265       .get_result::<Self>(conn)
266   }
267
268   fn unban(
269     conn: &PgConnection,
270     community_user_ban_form: &CommunityUserBanForm,
271   ) -> Result<usize, Error> {
272     use lemmy_db_schema::schema::community_user_ban::dsl::*;
273     diesel::delete(
274       community_user_ban
275         .filter(community_id.eq(community_user_ban_form.community_id))
276         .filter(user_id.eq(community_user_ban_form.user_id)),
277     )
278     .execute(conn)
279   }
280 }
281
282 impl Followable<CommunityFollowerForm> for CommunityFollower {
283   fn follow(
284     conn: &PgConnection,
285     community_follower_form: &CommunityFollowerForm,
286   ) -> Result<Self, Error> {
287     use lemmy_db_schema::schema::community_follower::dsl::*;
288     insert_into(community_follower)
289       .values(community_follower_form)
290       .on_conflict((community_id, user_id))
291       .do_update()
292       .set(community_follower_form)
293       .get_result::<Self>(conn)
294   }
295   fn follow_accepted(conn: &PgConnection, community_id_: i32, user_id_: i32) -> Result<Self, Error>
296   where
297     Self: Sized,
298   {
299     use lemmy_db_schema::schema::community_follower::dsl::*;
300     diesel::update(
301       community_follower
302         .filter(community_id.eq(community_id_))
303         .filter(user_id.eq(user_id_)),
304     )
305     .set(pending.eq(true))
306     .get_result::<Self>(conn)
307   }
308   fn unfollow(
309     conn: &PgConnection,
310     community_follower_form: &CommunityFollowerForm,
311   ) -> Result<usize, Error> {
312     use lemmy_db_schema::schema::community_follower::dsl::*;
313     diesel::delete(
314       community_follower
315         .filter(community_id.eq(&community_follower_form.community_id))
316         .filter(user_id.eq(&community_follower_form.user_id)),
317     )
318     .execute(conn)
319   }
320   // TODO: this function name only makes sense if you call it with a remote community. for a local
321   //       community, it will also return true if only remote followers exist
322   fn has_local_followers(conn: &PgConnection, community_id_: i32) -> Result<bool, Error> {
323     use lemmy_db_schema::schema::community_follower::dsl::*;
324     diesel::select(exists(
325       community_follower.filter(community_id.eq(community_id_)),
326     ))
327     .get_result(conn)
328   }
329 }
330
331 #[cfg(test)]
332 mod tests {
333   use crate::{
334     establish_unpooled_connection,
335     Bannable,
336     Crud,
337     Followable,
338     Joinable,
339     ListingType,
340     SortType,
341   };
342   use lemmy_db_schema::source::{community::*, user::*};
343
344   #[test]
345   fn test_crud() {
346     let conn = establish_unpooled_connection();
347
348     let new_user = UserForm {
349       name: "bobbee".into(),
350       preferred_username: None,
351       password_encrypted: "nope".into(),
352       email: None,
353       matrix_user_id: None,
354       avatar: None,
355       banner: None,
356       admin: false,
357       banned: Some(false),
358       published: None,
359       updated: None,
360       show_nsfw: false,
361       theme: "browser".into(),
362       default_sort_type: SortType::Hot as i16,
363       default_listing_type: ListingType::Subscribed as i16,
364       lang: "browser".into(),
365       show_avatars: true,
366       send_notifications_to_email: false,
367       actor_id: None,
368       bio: None,
369       local: true,
370       private_key: None,
371       public_key: None,
372       last_refreshed_at: None,
373       inbox_url: None,
374       shared_inbox_url: None,
375     };
376
377     let inserted_user = User_::create(&conn, &new_user).unwrap();
378
379     let new_community = CommunityForm {
380       name: "TIL".into(),
381       creator_id: inserted_user.id,
382       title: "nada".to_owned(),
383       description: None,
384       nsfw: false,
385       removed: None,
386       deleted: None,
387       updated: None,
388       actor_id: None,
389       local: true,
390       private_key: None,
391       public_key: None,
392       last_refreshed_at: None,
393       published: None,
394       icon: None,
395       banner: None,
396       followers_url: None,
397       inbox_url: None,
398       shared_inbox_url: None,
399     };
400
401     let inserted_community = Community::create(&conn, &new_community).unwrap();
402
403     let expected_community = Community {
404       id: inserted_community.id,
405       creator_id: inserted_user.id,
406       name: "TIL".into(),
407       title: "nada".to_owned(),
408       description: None,
409       nsfw: false,
410       removed: false,
411       deleted: false,
412       published: inserted_community.published,
413       updated: None,
414       actor_id: inserted_community.actor_id.to_owned(),
415       local: true,
416       private_key: None,
417       public_key: None,
418       last_refreshed_at: inserted_community.published,
419       icon: None,
420       banner: None,
421       followers_url: inserted_community.followers_url.to_owned(),
422       inbox_url: inserted_community.inbox_url.to_owned(),
423       shared_inbox_url: None,
424     };
425
426     let community_follower_form = CommunityFollowerForm {
427       community_id: inserted_community.id,
428       user_id: inserted_user.id,
429       pending: false,
430     };
431
432     let inserted_community_follower =
433       CommunityFollower::follow(&conn, &community_follower_form).unwrap();
434
435     let expected_community_follower = CommunityFollower {
436       id: inserted_community_follower.id,
437       community_id: inserted_community.id,
438       user_id: inserted_user.id,
439       pending: Some(false),
440       published: inserted_community_follower.published,
441     };
442
443     let community_user_form = CommunityModeratorForm {
444       community_id: inserted_community.id,
445       user_id: inserted_user.id,
446     };
447
448     let inserted_community_user = CommunityModerator::join(&conn, &community_user_form).unwrap();
449
450     let expected_community_user = CommunityModerator {
451       id: inserted_community_user.id,
452       community_id: inserted_community.id,
453       user_id: inserted_user.id,
454       published: inserted_community_user.published,
455     };
456
457     let community_user_ban_form = CommunityUserBanForm {
458       community_id: inserted_community.id,
459       user_id: inserted_user.id,
460     };
461
462     let inserted_community_user_ban =
463       CommunityUserBan::ban(&conn, &community_user_ban_form).unwrap();
464
465     let expected_community_user_ban = CommunityUserBan {
466       id: inserted_community_user_ban.id,
467       community_id: inserted_community.id,
468       user_id: inserted_user.id,
469       published: inserted_community_user_ban.published,
470     };
471
472     let read_community = Community::read(&conn, inserted_community.id).unwrap();
473     let updated_community =
474       Community::update(&conn, inserted_community.id, &new_community).unwrap();
475     let ignored_community = CommunityFollower::unfollow(&conn, &community_follower_form).unwrap();
476     let left_community = CommunityModerator::leave(&conn, &community_user_form).unwrap();
477     let unban = CommunityUserBan::unban(&conn, &community_user_ban_form).unwrap();
478     let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
479     User_::delete(&conn, inserted_user.id).unwrap();
480
481     assert_eq!(expected_community, read_community);
482     assert_eq!(expected_community, inserted_community);
483     assert_eq!(expected_community, updated_community);
484     assert_eq!(expected_community_follower, inserted_community_follower);
485     assert_eq!(expected_community_user, inserted_community_user);
486     assert_eq!(expected_community_user_ban, inserted_community_user_ban);
487     assert_eq!(1, ignored_community);
488     assert_eq!(1, left_community);
489     assert_eq!(1, unban);
490     // assert_eq!(2, loaded_count);
491     assert_eq!(1, num_deleted);
492   }
493 }