]> Untitled Git - lemmy.git/blob - crates/db_queries/src/source/community.rs
Still continuing on....
[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     CommunityPersonBan,
13     CommunityPersonBanForm,
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_moderator_form: &CommunityModeratorForm,
210   ) -> Result<Self, Error> {
211     use lemmy_db_schema::schema::community_moderator::dsl::*;
212     insert_into(community_moderator)
213       .values(community_moderator_form)
214       .get_result::<Self>(conn)
215   }
216
217   fn leave(
218     conn: &PgConnection,
219     community_moderator_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_moderator_form.community_id))
225         .filter(person_id.eq(community_moderator_form.person_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_person_moderated_communities(
234     conn: &PgConnection,
235     for_person_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_person_moderated_communities(
246     conn: &PgConnection,
247     for_person_id: i32,
248   ) -> Result<Vec<i32>, Error> {
249     use lemmy_db_schema::schema::community_moderator::dsl::*;
250     community_moderator
251       .filter(person_id.eq(for_person_id))
252       .select(community_id)
253       .load::<i32>(conn)
254   }
255 }
256
257 impl Bannable<CommunityPersonBanForm> for CommunityPersonBan {
258   fn ban(
259     conn: &PgConnection,
260     community_person_ban_form: &CommunityPersonBanForm,
261   ) -> Result<Self, Error> {
262     use lemmy_db_schema::schema::community_person_ban::dsl::*;
263     insert_into(community_person_ban)
264       .values(community_person_ban_form)
265       .get_result::<Self>(conn)
266   }
267
268   fn unban(
269     conn: &PgConnection,
270     community_person_ban_form: &CommunityPersonBanForm,
271   ) -> Result<usize, Error> {
272     use lemmy_db_schema::schema::community_person_ban::dsl::*;
273     diesel::delete(
274       community_person_ban
275         .filter(community_id.eq(community_person_ban_form.community_id))
276         .filter(person_id.eq(community_person_ban_form.person_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.person_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::*, person::*};
343
344   #[test]
345   fn test_crud() {
346     let conn = establish_unpooled_connection();
347
348     let new_person = PersonForm {
349       name: "bobbee".into(),
350       preferred_username: None,
351       avatar: None,
352       banner: None,
353       banned: Some(false),
354       deleted: false,
355       published: None,
356       updated: None,
357       actor_id: None,
358       bio: None,
359       local: true,
360       private_key: None,
361       public_key: None,
362       last_refreshed_at: None,
363       inbox_url: None,
364       shared_inbox_url: None,
365     };
366
367     let inserted_person = Person::create(&conn, &new_person).unwrap();
368
369     let new_community = CommunityForm {
370       name: "TIL".into(),
371       creator_id: inserted_person.id,
372       title: "nada".to_owned(),
373       description: None,
374       nsfw: false,
375       removed: None,
376       deleted: None,
377       updated: None,
378       actor_id: None,
379       local: true,
380       private_key: None,
381       public_key: None,
382       last_refreshed_at: None,
383       published: None,
384       icon: None,
385       banner: None,
386       followers_url: None,
387       inbox_url: None,
388       shared_inbox_url: None,
389     };
390
391     let inserted_community = Community::create(&conn, &new_community).unwrap();
392
393     let expected_community = Community {
394       id: inserted_community.id,
395       creator_id: inserted_person.id,
396       name: "TIL".into(),
397       title: "nada".to_owned(),
398       description: None,
399       nsfw: false,
400       removed: false,
401       deleted: false,
402       published: inserted_community.published,
403       updated: None,
404       actor_id: inserted_community.actor_id.to_owned(),
405       local: true,
406       private_key: None,
407       public_key: None,
408       last_refreshed_at: inserted_community.published,
409       icon: None,
410       banner: None,
411       followers_url: inserted_community.followers_url.to_owned(),
412       inbox_url: inserted_community.inbox_url.to_owned(),
413       shared_inbox_url: None,
414     };
415
416     let community_follower_form = CommunityFollowerForm {
417       community_id: inserted_community.id,
418       person_id: inserted_person.id,
419       pending: false,
420     };
421
422     let inserted_community_follower =
423       CommunityFollower::follow(&conn, &community_follower_form).unwrap();
424
425     let expected_community_follower = CommunityFollower {
426       id: inserted_community_follower.id,
427       community_id: inserted_community.id,
428       person_id: inserted_person.id,
429       pending: Some(false),
430       published: inserted_community_follower.published,
431     };
432
433     let community_moderator_form = CommunityModeratorForm {
434       community_id: inserted_community.id,
435       person_id: inserted_person.id,
436     };
437
438     let inserted_community_moderator = CommunityModerator::join(&conn, &community_moderator_form).unwrap();
439
440     let expected_community_moderator = CommunityModerator {
441       id: inserted_community_moderator.id,
442       community_id: inserted_community.id,
443       person_id: inserted_person.id,
444       published: inserted_community_moderator.published,
445     };
446
447     let community_person_ban_form = CommunityPersonBanForm {
448       community_id: inserted_community.id,
449       person_id: inserted_person.id,
450     };
451
452     let inserted_community_person_ban =
453       CommunityPersonBan::ban(&conn, &community_person_ban_form).unwrap();
454
455     let expected_community_person_ban = CommunityPersonBan {
456       id: inserted_community_person_ban.id,
457       community_id: inserted_community.id,
458       person_id: inserted_person.id,
459       published: inserted_community_person_ban.published,
460     };
461
462     let read_community = Community::read(&conn, inserted_community.id).unwrap();
463     let updated_community =
464       Community::update(&conn, inserted_community.id, &new_community).unwrap();
465     let ignored_community = CommunityFollower::unfollow(&conn, &community_follower_form).unwrap();
466     let left_community = CommunityModerator::leave(&conn, &community_moderator_form).unwrap();
467     let unban = CommunityPersonBan::unban(&conn, &community_person_ban_form).unwrap();
468     let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
469     Person::delete(&conn, inserted_person.id).unwrap();
470
471     assert_eq!(expected_community, read_community);
472     assert_eq!(expected_community, inserted_community);
473     assert_eq!(expected_community, updated_community);
474     assert_eq!(expected_community_follower, inserted_community_follower);
475     assert_eq!(expected_community_moderator, inserted_community_moderator);
476     assert_eq!(expected_community_person_ban, inserted_community_person_ban);
477     assert_eq!(1, ignored_community);
478     assert_eq!(1, left_community);
479     assert_eq!(1, unban);
480     // assert_eq!(2, loaded_count);
481     assert_eq!(1, num_deleted);
482   }
483 }