]> Untitled Git - lemmy.git/blob - crates/db_schema/src/impls/person.rs
Fixing `.drone.yml` (#2677)
[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 mod safe_type {
30   use crate::{
31     schema::person::columns::{
32       actor_id,
33       admin,
34       avatar,
35       ban_expires,
36       banned,
37       banner,
38       bio,
39       bot_account,
40       deleted,
41       display_name,
42       id,
43       inbox_url,
44       instance_id,
45       local,
46       matrix_user_id,
47       name,
48       published,
49       shared_inbox_url,
50       updated,
51     },
52     source::person::Person,
53     traits::ToSafe,
54   };
55
56   type Columns = (
57     id,
58     name,
59     display_name,
60     avatar,
61     banned,
62     published,
63     updated,
64     actor_id,
65     bio,
66     local,
67     banner,
68     deleted,
69     inbox_url,
70     shared_inbox_url,
71     matrix_user_id,
72     admin,
73     bot_account,
74     ban_expires,
75     instance_id,
76   );
77
78   impl ToSafe for Person {
79     type SafeColumns = Columns;
80     fn safe_columns_tuple() -> Self::SafeColumns {
81       (
82         id,
83         name,
84         display_name,
85         avatar,
86         banned,
87         published,
88         updated,
89         actor_id,
90         bio,
91         local,
92         banner,
93         deleted,
94         inbox_url,
95         shared_inbox_url,
96         matrix_user_id,
97         admin,
98         bot_account,
99         ban_expires,
100         instance_id,
101       )
102     }
103   }
104 }
105
106 #[async_trait]
107 impl Crud for Person {
108   type InsertForm = PersonInsertForm;
109   type UpdateForm = PersonUpdateForm;
110   type IdType = PersonId;
111   async fn read(pool: &DbPool, person_id: PersonId) -> Result<Self, Error> {
112     let conn = &mut get_conn(pool).await?;
113     person
114       .filter(deleted.eq(false))
115       .find(person_id)
116       .first::<Self>(conn)
117       .await
118   }
119   async fn delete(pool: &DbPool, person_id: PersonId) -> Result<usize, Error> {
120     let conn = &mut get_conn(pool).await?;
121     diesel::delete(person.find(person_id)).execute(conn).await
122   }
123   async fn create(pool: &DbPool, form: &PersonInsertForm) -> Result<Self, Error> {
124     let conn = &mut get_conn(pool).await?;
125     insert_into(person)
126       .values(form)
127       .on_conflict(actor_id)
128       .do_update()
129       .set(form)
130       .get_result::<Self>(conn)
131       .await
132   }
133   async fn update(
134     pool: &DbPool,
135     person_id: PersonId,
136     form: &PersonUpdateForm,
137   ) -> Result<Self, Error> {
138     let conn = &mut get_conn(pool).await?;
139     diesel::update(person.find(person_id))
140       .set(form)
141       .get_result::<Self>(conn)
142       .await
143   }
144 }
145
146 impl Person {
147   pub async fn delete_account(pool: &DbPool, person_id: PersonId) -> Result<Person, Error> {
148     use crate::schema::local_user;
149     let conn = &mut get_conn(pool).await?;
150
151     // Set the local user info to none
152     diesel::update(local_user::table.filter(local_user::person_id.eq(person_id)))
153       .set((
154         local_user::email.eq::<Option<String>>(None),
155         local_user::validator_time.eq(naive_now()),
156       ))
157       .execute(conn)
158       .await?;
159
160     diesel::update(person.find(person_id))
161       .set((
162         display_name.eq::<Option<String>>(None),
163         avatar.eq::<Option<String>>(None),
164         banner.eq::<Option<String>>(None),
165         bio.eq::<Option<String>>(None),
166         matrix_user_id.eq::<Option<String>>(None),
167         deleted.eq(true),
168         updated.eq(naive_now()),
169       ))
170       .get_result::<Self>(conn)
171       .await
172   }
173 }
174
175 pub fn is_banned(banned_: bool, expires: Option<chrono::NaiveDateTime>) -> bool {
176   if let Some(expires) = expires {
177     banned_ && expires.gt(&naive_now())
178   } else {
179     banned_
180   }
181 }
182
183 #[async_trait]
184 impl ApubActor for Person {
185   async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Option<Self>, Error> {
186     let conn = &mut get_conn(pool).await?;
187     Ok(
188       person
189         .filter(deleted.eq(false))
190         .filter(actor_id.eq(object_id))
191         .first::<Person>(conn)
192         .await
193         .ok()
194         .map(Into::into),
195     )
196   }
197
198   async fn read_from_name(
199     pool: &DbPool,
200     from_name: &str,
201     include_deleted: bool,
202   ) -> Result<Person, Error> {
203     let conn = &mut get_conn(pool).await?;
204     let mut q = person
205       .into_boxed()
206       .filter(local.eq(true))
207       .filter(lower(name).eq(lower(from_name)));
208     if !include_deleted {
209       q = q.filter(deleted.eq(false))
210     }
211     q.first::<Self>(conn).await
212   }
213
214   async fn read_from_name_and_domain(
215     pool: &DbPool,
216     person_name: &str,
217     protocol_domain: &str,
218   ) -> Result<Person, Error> {
219     let conn = &mut get_conn(pool).await?;
220     person
221       .filter(lower(name).eq(lower(person_name)))
222       .filter(actor_id.like(format!("{protocol_domain}%")))
223       .first::<Self>(conn)
224       .await
225   }
226 }
227
228 #[async_trait]
229 impl Followable for PersonFollower {
230   type Form = PersonFollowerForm;
231   async fn follow(pool: &DbPool, form: &PersonFollowerForm) -> Result<Self, Error> {
232     use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
233     let conn = &mut get_conn(pool).await?;
234     insert_into(person_follower)
235       .values(form)
236       .on_conflict((follower_id, person_id))
237       .do_update()
238       .set(form)
239       .get_result::<Self>(conn)
240       .await
241   }
242   async fn follow_accepted(_: &DbPool, _: CommunityId, _: PersonId) -> Result<Self, Error> {
243     unimplemented!()
244   }
245   async fn unfollow(pool: &DbPool, form: &PersonFollowerForm) -> Result<usize, Error> {
246     use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
247     let conn = &mut get_conn(pool).await?;
248     diesel::delete(
249       person_follower
250         .filter(follower_id.eq(&form.follower_id))
251         .filter(person_id.eq(&form.person_id)),
252     )
253     .execute(conn)
254     .await
255   }
256 }
257
258 impl PersonFollower {
259   pub async fn list_followers(pool: &DbPool, person_id_: PersonId) -> Result<Vec<Person>, Error> {
260     use crate::schema::{person, person_follower, person_follower::person_id};
261     let conn = &mut get_conn(pool).await?;
262     person_follower::table
263       .inner_join(person::table)
264       .filter(person_id.eq(person_id_))
265       .select(person::all_columns)
266       .load(conn)
267       .await
268   }
269 }
270
271 #[cfg(test)]
272 mod tests {
273   use crate::{
274     source::{
275       instance::Instance,
276       person::{Person, PersonFollower, PersonFollowerForm, PersonInsertForm, PersonUpdateForm},
277     },
278     traits::{Crud, Followable},
279     utils::build_db_pool_for_tests,
280   };
281   use serial_test::serial;
282
283   #[tokio::test]
284   #[serial]
285   async fn test_crud() {
286     let pool = &build_db_pool_for_tests().await;
287
288     let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
289
290     let new_person = PersonInsertForm::builder()
291       .name("holly".into())
292       .public_key("nada".to_owned())
293       .instance_id(inserted_instance.id)
294       .build();
295
296     let inserted_person = Person::create(pool, &new_person).await.unwrap();
297
298     let expected_person = Person {
299       id: inserted_person.id,
300       name: "holly".into(),
301       display_name: None,
302       avatar: None,
303       banner: None,
304       banned: false,
305       deleted: false,
306       published: inserted_person.published,
307       updated: None,
308       actor_id: inserted_person.actor_id.clone(),
309       bio: None,
310       local: true,
311       bot_account: false,
312       admin: false,
313       private_key: None,
314       public_key: "nada".to_owned(),
315       last_refreshed_at: inserted_person.published,
316       inbox_url: inserted_person.inbox_url.clone(),
317       shared_inbox_url: None,
318       matrix_user_id: None,
319       ban_expires: None,
320       instance_id: inserted_instance.id,
321     };
322
323     let read_person = Person::read(pool, inserted_person.id).await.unwrap();
324
325     let update_person_form = PersonUpdateForm::builder()
326       .actor_id(Some(inserted_person.actor_id.clone()))
327       .build();
328     let updated_person = Person::update(pool, inserted_person.id, &update_person_form)
329       .await
330       .unwrap();
331
332     let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
333     Instance::delete(pool, inserted_instance.id).await.unwrap();
334
335     assert_eq!(expected_person, read_person);
336     assert_eq!(expected_person, inserted_person);
337     assert_eq!(expected_person, updated_person);
338     assert_eq!(1, num_deleted);
339   }
340
341   #[tokio::test]
342   #[serial]
343   async fn follow() {
344     let pool = &build_db_pool_for_tests().await;
345     let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
346
347     let person_form_1 = PersonInsertForm::builder()
348       .name("erich".into())
349       .public_key("pubkey".to_string())
350       .instance_id(inserted_instance.id)
351       .build();
352     let person_1 = Person::create(pool, &person_form_1).await.unwrap();
353     let person_form_2 = PersonInsertForm::builder()
354       .name("michele".into())
355       .public_key("pubkey".to_string())
356       .instance_id(inserted_instance.id)
357       .build();
358     let person_2 = Person::create(pool, &person_form_2).await.unwrap();
359
360     let follow_form = PersonFollowerForm {
361       person_id: person_1.id,
362       follower_id: person_2.id,
363       pending: false,
364     };
365     let person_follower = PersonFollower::follow(pool, &follow_form).await.unwrap();
366     assert_eq!(person_1.id, person_follower.person_id);
367     assert_eq!(person_2.id, person_follower.follower_id);
368     assert!(!person_follower.pending);
369
370     let followers = PersonFollower::list_followers(pool, person_1.id)
371       .await
372       .unwrap();
373     assert_eq!(vec![person_2], followers);
374
375     let unfollow = PersonFollower::unfollow(pool, &follow_form).await.unwrap();
376     assert_eq!(1, unfollow);
377   }
378 }