]> Untitled Git - lemmy.git/blob - crates/db_schema/src/impls/person.rs
032e2e0179ab4cedaf26ed340e1cfa8fe0e2da74
[lemmy.git] / crates / db_schema / src / impls / person.rs
1 use crate::{
2   newtypes::{CommunityId, DbUrl, PersonId},
3   schema::person::dsl::{
4     actor_id,
5     avatar,
6     banner,
7     bio,
8     deleted,
9     display_name,
10     local,
11     matrix_user_id,
12     name,
13     person,
14     updated,
15   },
16   source::person::{
17     Person,
18     PersonFollower,
19     PersonFollowerForm,
20     PersonInsertForm,
21     PersonUpdateForm,
22   },
23   traits::{ApubActor, Crud, Followable},
24   utils::{functions::lower, get_conn, naive_now, DbPool},
25 };
26 use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl, TextExpressionMethods};
27 use diesel_async::RunQueryDsl;
28
29 #[async_trait]
30 impl Crud for Person {
31   type InsertForm = PersonInsertForm;
32   type UpdateForm = PersonUpdateForm;
33   type IdType = PersonId;
34   async fn read(pool: &DbPool, person_id: PersonId) -> Result<Self, Error> {
35     let conn = &mut get_conn(pool).await?;
36     person
37       .filter(deleted.eq(false))
38       .find(person_id)
39       .first::<Self>(conn)
40       .await
41   }
42   async fn delete(pool: &DbPool, person_id: PersonId) -> Result<usize, Error> {
43     let conn = &mut get_conn(pool).await?;
44     diesel::delete(person.find(person_id)).execute(conn).await
45   }
46   async fn create(pool: &DbPool, form: &PersonInsertForm) -> Result<Self, Error> {
47     let conn = &mut get_conn(pool).await?;
48     insert_into(person)
49       .values(form)
50       .on_conflict(actor_id)
51       .do_update()
52       .set(form)
53       .get_result::<Self>(conn)
54       .await
55   }
56   async fn update(
57     pool: &DbPool,
58     person_id: PersonId,
59     form: &PersonUpdateForm,
60   ) -> Result<Self, Error> {
61     let conn = &mut get_conn(pool).await?;
62     diesel::update(person.find(person_id))
63       .set(form)
64       .get_result::<Self>(conn)
65       .await
66   }
67 }
68
69 impl Person {
70   pub async fn delete_account(pool: &DbPool, person_id: PersonId) -> Result<Person, Error> {
71     use crate::schema::local_user;
72     let conn = &mut get_conn(pool).await?;
73
74     // Set the local user info to none
75     diesel::update(local_user::table.filter(local_user::person_id.eq(person_id)))
76       .set((
77         local_user::email.eq::<Option<String>>(None),
78         local_user::validator_time.eq(naive_now()),
79       ))
80       .execute(conn)
81       .await?;
82
83     diesel::update(person.find(person_id))
84       .set((
85         display_name.eq::<Option<String>>(None),
86         avatar.eq::<Option<String>>(None),
87         banner.eq::<Option<String>>(None),
88         bio.eq::<Option<String>>(None),
89         matrix_user_id.eq::<Option<String>>(None),
90         deleted.eq(true),
91         updated.eq(naive_now()),
92       ))
93       .get_result::<Self>(conn)
94       .await
95   }
96 }
97
98 pub fn is_banned(banned_: bool, expires: Option<chrono::NaiveDateTime>) -> bool {
99   if let Some(expires) = expires {
100     banned_ && expires.gt(&naive_now())
101   } else {
102     banned_
103   }
104 }
105
106 #[async_trait]
107 impl ApubActor for Person {
108   async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Option<Self>, Error> {
109     let conn = &mut get_conn(pool).await?;
110     Ok(
111       person
112         .filter(deleted.eq(false))
113         .filter(actor_id.eq(object_id))
114         .first::<Person>(conn)
115         .await
116         .ok()
117         .map(Into::into),
118     )
119   }
120
121   async fn read_from_name(
122     pool: &DbPool,
123     from_name: &str,
124     include_deleted: bool,
125   ) -> Result<Person, Error> {
126     let conn = &mut get_conn(pool).await?;
127     let mut q = person
128       .into_boxed()
129       .filter(local.eq(true))
130       .filter(lower(name).eq(lower(from_name)));
131     if !include_deleted {
132       q = q.filter(deleted.eq(false))
133     }
134     q.first::<Self>(conn).await
135   }
136
137   async fn read_from_name_and_domain(
138     pool: &DbPool,
139     person_name: &str,
140     protocol_domain: &str,
141   ) -> Result<Person, Error> {
142     let conn = &mut get_conn(pool).await?;
143     person
144       .filter(lower(name).eq(lower(person_name)))
145       .filter(actor_id.like(format!("{protocol_domain}%")))
146       .first::<Self>(conn)
147       .await
148   }
149 }
150
151 #[async_trait]
152 impl Followable for PersonFollower {
153   type Form = PersonFollowerForm;
154   async fn follow(pool: &DbPool, form: &PersonFollowerForm) -> Result<Self, Error> {
155     use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
156     let conn = &mut get_conn(pool).await?;
157     insert_into(person_follower)
158       .values(form)
159       .on_conflict((follower_id, person_id))
160       .do_update()
161       .set(form)
162       .get_result::<Self>(conn)
163       .await
164   }
165   async fn follow_accepted(_: &DbPool, _: CommunityId, _: PersonId) -> Result<Self, Error> {
166     unimplemented!()
167   }
168   async fn unfollow(pool: &DbPool, form: &PersonFollowerForm) -> Result<usize, Error> {
169     use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
170     let conn = &mut get_conn(pool).await?;
171     diesel::delete(
172       person_follower
173         .filter(follower_id.eq(&form.follower_id))
174         .filter(person_id.eq(&form.person_id)),
175     )
176     .execute(conn)
177     .await
178   }
179 }
180
181 impl PersonFollower {
182   pub async fn list_followers(pool: &DbPool, person_id_: PersonId) -> Result<Vec<Person>, Error> {
183     use crate::schema::{person, person_follower, person_follower::person_id};
184     let conn = &mut get_conn(pool).await?;
185     person_follower::table
186       .inner_join(person::table)
187       .filter(person_id.eq(person_id_))
188       .select(person::all_columns)
189       .load(conn)
190       .await
191   }
192 }
193
194 #[cfg(test)]
195 mod tests {
196   use crate::{
197     source::{
198       instance::Instance,
199       person::{Person, PersonFollower, PersonFollowerForm, PersonInsertForm, PersonUpdateForm},
200     },
201     traits::{Crud, Followable},
202     utils::build_db_pool_for_tests,
203   };
204   use serial_test::serial;
205
206   #[tokio::test]
207   #[serial]
208   async fn test_crud() {
209     let pool = &build_db_pool_for_tests().await;
210
211     let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
212       .await
213       .unwrap();
214
215     let new_person = PersonInsertForm::builder()
216       .name("holly".into())
217       .public_key("nada".to_owned())
218       .instance_id(inserted_instance.id)
219       .build();
220
221     let inserted_person = Person::create(pool, &new_person).await.unwrap();
222
223     let expected_person = Person {
224       id: inserted_person.id,
225       name: "holly".into(),
226       display_name: None,
227       avatar: None,
228       banner: None,
229       banned: false,
230       deleted: false,
231       published: inserted_person.published,
232       updated: None,
233       actor_id: inserted_person.actor_id.clone(),
234       bio: None,
235       local: true,
236       bot_account: false,
237       admin: false,
238       private_key: None,
239       public_key: "nada".to_owned(),
240       last_refreshed_at: inserted_person.published,
241       inbox_url: inserted_person.inbox_url.clone(),
242       shared_inbox_url: None,
243       matrix_user_id: None,
244       ban_expires: None,
245       instance_id: inserted_instance.id,
246     };
247
248     let read_person = Person::read(pool, inserted_person.id).await.unwrap();
249
250     let update_person_form = PersonUpdateForm::builder()
251       .actor_id(Some(inserted_person.actor_id.clone()))
252       .build();
253     let updated_person = Person::update(pool, inserted_person.id, &update_person_form)
254       .await
255       .unwrap();
256
257     let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
258     Instance::delete(pool, inserted_instance.id).await.unwrap();
259
260     assert_eq!(expected_person, read_person);
261     assert_eq!(expected_person, inserted_person);
262     assert_eq!(expected_person, updated_person);
263     assert_eq!(1, num_deleted);
264   }
265
266   #[tokio::test]
267   #[serial]
268   async fn follow() {
269     let pool = &build_db_pool_for_tests().await;
270     let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
271       .await
272       .unwrap();
273
274     let person_form_1 = PersonInsertForm::builder()
275       .name("erich".into())
276       .public_key("pubkey".to_string())
277       .instance_id(inserted_instance.id)
278       .build();
279     let person_1 = Person::create(pool, &person_form_1).await.unwrap();
280     let person_form_2 = PersonInsertForm::builder()
281       .name("michele".into())
282       .public_key("pubkey".to_string())
283       .instance_id(inserted_instance.id)
284       .build();
285     let person_2 = Person::create(pool, &person_form_2).await.unwrap();
286
287     let follow_form = PersonFollowerForm {
288       person_id: person_1.id,
289       follower_id: person_2.id,
290       pending: false,
291     };
292     let person_follower = PersonFollower::follow(pool, &follow_form).await.unwrap();
293     assert_eq!(person_1.id, person_follower.person_id);
294     assert_eq!(person_2.id, person_follower.follower_id);
295     assert!(!person_follower.pending);
296
297     let followers = PersonFollower::list_followers(pool, person_1.id)
298       .await
299       .unwrap();
300     assert_eq!(vec![person_2], followers);
301
302     let unfollow = PersonFollower::unfollow(pool, &follow_form).await.unwrap();
303     assert_eq!(1, unfollow);
304   }
305 }