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