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