]> Untitled Git - lemmy.git/blob - crates/db_schema/src/impls/community.rs
Dropping default on pending column.
[lemmy.git] / crates / db_schema / src / impls / community.rs
1 use crate::{
2   newtypes::{CommunityId, DbUrl, PersonId},
3   source::community::{
4     Community,
5     CommunityFollower,
6     CommunityFollowerForm,
7     CommunityForm,
8     CommunityModerator,
9     CommunityModeratorForm,
10     CommunityPersonBan,
11     CommunityPersonBanForm,
12     CommunitySafe,
13   },
14   traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
15   utils::{functions::lower, naive_now},
16 };
17 use diesel::{
18   dsl::*,
19   result::Error,
20   ExpressionMethods,
21   PgConnection,
22   QueryDsl,
23   RunQueryDsl,
24   TextExpressionMethods,
25 };
26
27 mod safe_type {
28   use crate::{schema::community::*, source::community::Community, traits::ToSafe};
29
30   type Columns = (
31     id,
32     name,
33     title,
34     description,
35     removed,
36     published,
37     updated,
38     deleted,
39     nsfw,
40     actor_id,
41     local,
42     icon,
43     banner,
44     hidden,
45     posting_restricted_to_mods,
46   );
47
48   impl ToSafe for Community {
49     type SafeColumns = Columns;
50     fn safe_columns_tuple() -> Self::SafeColumns {
51       (
52         id,
53         name,
54         title,
55         description,
56         removed,
57         published,
58         updated,
59         deleted,
60         nsfw,
61         actor_id,
62         local,
63         icon,
64         banner,
65         hidden,
66         posting_restricted_to_mods,
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: &DbUrl) -> Result<Option<Self>, Error> {
295     use crate::schema::community::dsl::*;
296     Ok(
297       community
298         .filter(actor_id.eq(object_id))
299         .first::<Community>(conn)
300         .ok()
301         .map(Into::into),
302     )
303   }
304
305   fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error> {
306     use crate::schema::community::dsl::*;
307     community
308       .filter(local.eq(true))
309       .filter(lower(name).eq(lower(community_name)))
310       .first::<Self>(conn)
311   }
312
313   fn read_from_name_and_domain(
314     conn: &PgConnection,
315     community_name: &str,
316     protocol_domain: &str,
317   ) -> Result<Community, Error> {
318     use crate::schema::community::dsl::*;
319     community
320       .filter(lower(name).eq(lower(community_name)))
321       .filter(actor_id.like(format!("{}%", protocol_domain)))
322       .first::<Self>(conn)
323   }
324 }
325
326 #[cfg(test)]
327 mod tests {
328   use crate::{
329     source::{community::*, person::*},
330     traits::{Bannable, Crud, Followable, Joinable},
331     utils::establish_unpooled_connection,
332   };
333   use serial_test::serial;
334
335   #[test]
336   #[serial]
337   fn test_crud() {
338     let conn = establish_unpooled_connection();
339
340     let new_person = PersonForm {
341       name: "bobbee".into(),
342       ..PersonForm::default()
343     };
344
345     let inserted_person = Person::create(&conn, &new_person).unwrap();
346
347     let new_community = CommunityForm {
348       name: "TIL".into(),
349       title: "nada".to_owned(),
350       public_key: "nada".to_owned(),
351       ..CommunityForm::default()
352     };
353
354     let inserted_community = Community::create(&conn, &new_community).unwrap();
355
356     let expected_community = Community {
357       id: inserted_community.id,
358       name: "TIL".into(),
359       title: "nada".to_owned(),
360       description: None,
361       nsfw: false,
362       removed: false,
363       deleted: false,
364       published: inserted_community.published,
365       updated: None,
366       actor_id: inserted_community.actor_id.to_owned(),
367       local: true,
368       private_key: None,
369       public_key: "nada".to_owned(),
370       last_refreshed_at: inserted_community.published,
371       icon: None,
372       banner: None,
373       followers_url: inserted_community.followers_url.to_owned(),
374       inbox_url: inserted_community.inbox_url.to_owned(),
375       shared_inbox_url: None,
376       hidden: false,
377       posting_restricted_to_mods: 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: 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 }