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