]> Untitled Git - lemmy.git/blob - crates/db_queries/src/source/community.rs
Rewrite delete activities (#1699)
[lemmy.git] / crates / db_queries / src / source / community.rs
1 use crate::{ApubObject, Bannable, Crud, DeleteableOrRemoveable, 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     CommunitySafe,
15   },
16   CommunityId,
17   DbUrl,
18   PersonId,
19 };
20
21 mod safe_type {
22   use crate::{source::community::Community, ToSafe};
23   use lemmy_db_schema::schema::community::*;
24
25   type Columns = (
26     id,
27     name,
28     title,
29     description,
30     removed,
31     published,
32     updated,
33     deleted,
34     nsfw,
35     actor_id,
36     local,
37     icon,
38     banner,
39   );
40
41   impl ToSafe for Community {
42     type SafeColumns = Columns;
43     fn safe_columns_tuple() -> Self::SafeColumns {
44       (
45         id,
46         name,
47         title,
48         description,
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 for Community {
64   type Form = CommunityForm;
65   type IdType = CommunityId;
66   fn read(conn: &PgConnection, community_id: CommunityId) -> Result<Self, Error> {
67     use lemmy_db_schema::schema::community::dsl::*;
68     community.find(community_id).first::<Self>(conn)
69   }
70
71   fn delete(conn: &PgConnection, community_id: CommunityId) -> Result<usize, Error> {
72     use lemmy_db_schema::schema::community::dsl::*;
73     diesel::delete(community.find(community_id)).execute(conn)
74   }
75
76   fn create(conn: &PgConnection, new_community: &CommunityForm) -> Result<Self, Error> {
77     use lemmy_db_schema::schema::community::dsl::*;
78     insert_into(community)
79       .values(new_community)
80       .get_result::<Self>(conn)
81   }
82
83   fn update(
84     conn: &PgConnection,
85     community_id: CommunityId,
86     new_community: &CommunityForm,
87   ) -> Result<Self, Error> {
88     use lemmy_db_schema::schema::community::dsl::*;
89     diesel::update(community.find(community_id))
90       .set(new_community)
91       .get_result::<Self>(conn)
92   }
93 }
94
95 impl ApubObject for Community {
96   type Form = CommunityForm;
97   fn read_from_apub_id(conn: &PgConnection, for_actor_id: &DbUrl) -> Result<Self, Error> {
98     use lemmy_db_schema::schema::community::dsl::*;
99     community
100       .filter(actor_id.eq(for_actor_id))
101       .first::<Self>(conn)
102   }
103
104   fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
105     use lemmy_db_schema::schema::community::dsl::*;
106     insert_into(community)
107       .values(community_form)
108       .on_conflict(actor_id)
109       .do_update()
110       .set(community_form)
111       .get_result::<Self>(conn)
112   }
113 }
114
115 pub trait Community_ {
116   fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error>;
117   fn update_deleted(
118     conn: &PgConnection,
119     community_id: CommunityId,
120     new_deleted: bool,
121   ) -> Result<Community, Error>;
122   fn update_removed(
123     conn: &PgConnection,
124     community_id: CommunityId,
125     new_removed: bool,
126   ) -> Result<Community, Error>;
127   fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>;
128   fn read_from_followers_url(
129     conn: &PgConnection,
130     followers_url: &DbUrl,
131   ) -> Result<Community, Error>;
132 }
133
134 impl Community_ for Community {
135   fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error> {
136     use lemmy_db_schema::schema::community::dsl::*;
137     community
138       .filter(local.eq(true))
139       .filter(name.eq(community_name))
140       .first::<Self>(conn)
141   }
142
143   fn update_deleted(
144     conn: &PgConnection,
145     community_id: CommunityId,
146     new_deleted: bool,
147   ) -> Result<Community, Error> {
148     use lemmy_db_schema::schema::community::dsl::*;
149     diesel::update(community.find(community_id))
150       .set((deleted.eq(new_deleted), updated.eq(naive_now())))
151       .get_result::<Self>(conn)
152   }
153
154   fn update_removed(
155     conn: &PgConnection,
156     community_id: CommunityId,
157     new_removed: bool,
158   ) -> Result<Community, Error> {
159     use lemmy_db_schema::schema::community::dsl::*;
160     diesel::update(community.find(community_id))
161       .set((removed.eq(new_removed), updated.eq(naive_now())))
162       .get_result::<Self>(conn)
163   }
164
165   fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error> {
166     use lemmy_db_schema::schema::community::dsl::*;
167     community.select(actor_id).distinct().load::<String>(conn)
168   }
169
170   fn read_from_followers_url(
171     conn: &PgConnection,
172     followers_url_: &DbUrl,
173   ) -> Result<Community, Error> {
174     use lemmy_db_schema::schema::community::dsl::*;
175     community
176       .filter(followers_url.eq(followers_url_))
177       .first::<Self>(conn)
178   }
179 }
180
181 impl Joinable for CommunityModerator {
182   type Form = CommunityModeratorForm;
183   fn join(
184     conn: &PgConnection,
185     community_moderator_form: &CommunityModeratorForm,
186   ) -> Result<Self, Error> {
187     use lemmy_db_schema::schema::community_moderator::dsl::*;
188     insert_into(community_moderator)
189       .values(community_moderator_form)
190       .get_result::<Self>(conn)
191   }
192
193   fn leave(
194     conn: &PgConnection,
195     community_moderator_form: &CommunityModeratorForm,
196   ) -> Result<usize, Error> {
197     use lemmy_db_schema::schema::community_moderator::dsl::*;
198     diesel::delete(
199       community_moderator
200         .filter(community_id.eq(community_moderator_form.community_id))
201         .filter(person_id.eq(community_moderator_form.person_id)),
202     )
203     .execute(conn)
204   }
205 }
206
207 impl DeleteableOrRemoveable for CommunitySafe {
208   fn blank_out_deleted_or_removed_info(mut self) -> Self {
209     self.title = "".into();
210     self.description = None;
211     self.icon = None;
212     self.banner = None;
213     self
214   }
215 }
216
217 impl DeleteableOrRemoveable for Community {
218   fn blank_out_deleted_or_removed_info(mut self) -> Self {
219     self.title = "".into();
220     self.description = None;
221     self.icon = None;
222     self.banner = None;
223     self
224   }
225 }
226
227 pub trait CommunityModerator_ {
228   fn delete_for_community(
229     conn: &PgConnection,
230     for_community_id: CommunityId,
231   ) -> Result<usize, Error>;
232   fn get_person_moderated_communities(
233     conn: &PgConnection,
234     for_person_id: PersonId,
235   ) -> Result<Vec<CommunityId>, Error>;
236 }
237
238 impl CommunityModerator_ for CommunityModerator {
239   fn delete_for_community(
240     conn: &PgConnection,
241     for_community_id: CommunityId,
242   ) -> Result<usize, Error> {
243     use lemmy_db_schema::schema::community_moderator::dsl::*;
244     diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
245   }
246
247   fn get_person_moderated_communities(
248     conn: &PgConnection,
249     for_person_id: PersonId,
250   ) -> Result<Vec<CommunityId>, Error> {
251     use lemmy_db_schema::schema::community_moderator::dsl::*;
252     community_moderator
253       .filter(person_id.eq(for_person_id))
254       .select(community_id)
255       .load::<CommunityId>(conn)
256   }
257 }
258
259 impl Bannable for CommunityPersonBan {
260   type Form = CommunityPersonBanForm;
261   fn ban(
262     conn: &PgConnection,
263     community_person_ban_form: &CommunityPersonBanForm,
264   ) -> Result<Self, Error> {
265     use lemmy_db_schema::schema::community_person_ban::dsl::*;
266     insert_into(community_person_ban)
267       .values(community_person_ban_form)
268       .get_result::<Self>(conn)
269   }
270
271   fn unban(
272     conn: &PgConnection,
273     community_person_ban_form: &CommunityPersonBanForm,
274   ) -> Result<usize, Error> {
275     use lemmy_db_schema::schema::community_person_ban::dsl::*;
276     diesel::delete(
277       community_person_ban
278         .filter(community_id.eq(community_person_ban_form.community_id))
279         .filter(person_id.eq(community_person_ban_form.person_id)),
280     )
281     .execute(conn)
282   }
283 }
284
285 impl Followable for CommunityFollower {
286   type Form = CommunityFollowerForm;
287   fn follow(
288     conn: &PgConnection,
289     community_follower_form: &CommunityFollowerForm,
290   ) -> Result<Self, Error> {
291     use lemmy_db_schema::schema::community_follower::dsl::*;
292     insert_into(community_follower)
293       .values(community_follower_form)
294       .on_conflict((community_id, person_id))
295       .do_update()
296       .set(community_follower_form)
297       .get_result::<Self>(conn)
298   }
299   fn follow_accepted(
300     conn: &PgConnection,
301     community_id_: CommunityId,
302     person_id_: PersonId,
303   ) -> Result<Self, Error>
304   where
305     Self: Sized,
306   {
307     use lemmy_db_schema::schema::community_follower::dsl::*;
308     diesel::update(
309       community_follower
310         .filter(community_id.eq(community_id_))
311         .filter(person_id.eq(person_id_)),
312     )
313     .set(pending.eq(true))
314     .get_result::<Self>(conn)
315   }
316   fn unfollow(
317     conn: &PgConnection,
318     community_follower_form: &CommunityFollowerForm,
319   ) -> Result<usize, Error> {
320     use lemmy_db_schema::schema::community_follower::dsl::*;
321     diesel::delete(
322       community_follower
323         .filter(community_id.eq(&community_follower_form.community_id))
324         .filter(person_id.eq(&community_follower_form.person_id)),
325     )
326     .execute(conn)
327   }
328   // TODO: this function name only makes sense if you call it with a remote community. for a local
329   //       community, it will also return true if only remote followers exist
330   fn has_local_followers(conn: &PgConnection, community_id_: CommunityId) -> Result<bool, Error> {
331     use lemmy_db_schema::schema::community_follower::dsl::*;
332     diesel::select(exists(
333       community_follower.filter(community_id.eq(community_id_)),
334     ))
335     .get_result(conn)
336   }
337 }
338
339 #[cfg(test)]
340 mod tests {
341   use crate::{establish_unpooled_connection, Bannable, Crud, Followable, Joinable};
342   use lemmy_db_schema::source::{community::*, person::*};
343   use serial_test::serial;
344
345   #[test]
346   #[serial]
347   fn test_crud() {
348     let conn = establish_unpooled_connection();
349
350     let new_person = PersonForm {
351       name: "bobbee".into(),
352       ..PersonForm::default()
353     };
354
355     let inserted_person = Person::create(&conn, &new_person).unwrap();
356
357     let new_community = CommunityForm {
358       name: "TIL".into(),
359       title: "nada".to_owned(),
360       ..CommunityForm::default()
361     };
362
363     let inserted_community = Community::create(&conn, &new_community).unwrap();
364
365     let expected_community = Community {
366       id: inserted_community.id,
367       name: "TIL".into(),
368       title: "nada".to_owned(),
369       description: None,
370       nsfw: false,
371       removed: false,
372       deleted: false,
373       published: inserted_community.published,
374       updated: None,
375       actor_id: inserted_community.actor_id.to_owned(),
376       local: true,
377       private_key: None,
378       public_key: None,
379       last_refreshed_at: inserted_community.published,
380       icon: None,
381       banner: None,
382       followers_url: inserted_community.followers_url.to_owned(),
383       inbox_url: inserted_community.inbox_url.to_owned(),
384       shared_inbox_url: None,
385     };
386
387     let community_follower_form = CommunityFollowerForm {
388       community_id: inserted_community.id,
389       person_id: inserted_person.id,
390       pending: false,
391     };
392
393     let inserted_community_follower =
394       CommunityFollower::follow(&conn, &community_follower_form).unwrap();
395
396     let expected_community_follower = CommunityFollower {
397       id: inserted_community_follower.id,
398       community_id: inserted_community.id,
399       person_id: inserted_person.id,
400       pending: Some(false),
401       published: inserted_community_follower.published,
402     };
403
404     let community_moderator_form = CommunityModeratorForm {
405       community_id: inserted_community.id,
406       person_id: inserted_person.id,
407     };
408
409     let inserted_community_moderator =
410       CommunityModerator::join(&conn, &community_moderator_form).unwrap();
411
412     let expected_community_moderator = CommunityModerator {
413       id: inserted_community_moderator.id,
414       community_id: inserted_community.id,
415       person_id: inserted_person.id,
416       published: inserted_community_moderator.published,
417     };
418
419     let community_person_ban_form = CommunityPersonBanForm {
420       community_id: inserted_community.id,
421       person_id: inserted_person.id,
422     };
423
424     let inserted_community_person_ban =
425       CommunityPersonBan::ban(&conn, &community_person_ban_form).unwrap();
426
427     let expected_community_person_ban = CommunityPersonBan {
428       id: inserted_community_person_ban.id,
429       community_id: inserted_community.id,
430       person_id: inserted_person.id,
431       published: inserted_community_person_ban.published,
432     };
433
434     let read_community = Community::read(&conn, inserted_community.id).unwrap();
435     let updated_community =
436       Community::update(&conn, inserted_community.id, &new_community).unwrap();
437     let ignored_community = CommunityFollower::unfollow(&conn, &community_follower_form).unwrap();
438     let left_community = CommunityModerator::leave(&conn, &community_moderator_form).unwrap();
439     let unban = CommunityPersonBan::unban(&conn, &community_person_ban_form).unwrap();
440     let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
441     Person::delete(&conn, inserted_person.id).unwrap();
442
443     assert_eq!(expected_community, read_community);
444     assert_eq!(expected_community, inserted_community);
445     assert_eq!(expected_community, updated_community);
446     assert_eq!(expected_community_follower, inserted_community_follower);
447     assert_eq!(expected_community_moderator, inserted_community_moderator);
448     assert_eq!(expected_community_person_ban, inserted_community_person_ban);
449     assert_eq!(1, ignored_community);
450     assert_eq!(1, left_community);
451     assert_eq!(1, unban);
452     // assert_eq!(2, loaded_count);
453     assert_eq!(1, num_deleted);
454   }
455 }