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