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