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