]> Untitled Git - lemmy.git/blob - crates/db_queries/src/source/activity.rs
Use URL type in most outstanding struct fields (#1468)
[lemmy.git] / crates / db_queries / src / source / activity.rs
1 use crate::Crud;
2 use diesel::{dsl::*, result::Error, sql_types::Text, *};
3 use lemmy_db_schema::{source::activity::*, DbUrl};
4 use log::debug;
5 use serde::Serialize;
6 use serde_json::Value;
7 use std::{
8   fmt::Debug,
9   io::{Error as IoError, ErrorKind},
10 };
11
12 impl Crud<ActivityForm> for Activity {
13   fn read(conn: &PgConnection, activity_id: i32) -> Result<Self, Error> {
14     use lemmy_db_schema::schema::activity::dsl::*;
15     activity.find(activity_id).first::<Self>(conn)
16   }
17
18   fn create(conn: &PgConnection, new_activity: &ActivityForm) -> Result<Self, Error> {
19     use lemmy_db_schema::schema::activity::dsl::*;
20     insert_into(activity)
21       .values(new_activity)
22       .get_result::<Self>(conn)
23   }
24
25   fn update(
26     conn: &PgConnection,
27     activity_id: i32,
28     new_activity: &ActivityForm,
29   ) -> Result<Self, Error> {
30     use lemmy_db_schema::schema::activity::dsl::*;
31     diesel::update(activity.find(activity_id))
32       .set(new_activity)
33       .get_result::<Self>(conn)
34   }
35   fn delete(conn: &PgConnection, activity_id: i32) -> Result<usize, Error> {
36     use lemmy_db_schema::schema::activity::dsl::*;
37     diesel::delete(activity.find(activity_id)).execute(conn)
38   }
39 }
40
41 pub trait Activity_ {
42   fn insert<T>(
43     conn: &PgConnection,
44     ap_id: DbUrl,
45     data: &T,
46     local: bool,
47     sensitive: bool,
48   ) -> Result<Activity, IoError>
49   where
50     T: Serialize + Debug;
51
52   fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error>;
53   fn delete_olds(conn: &PgConnection) -> Result<usize, Error>;
54
55   /// Returns up to 20 activities of type `Announce/Create/Page` from the community
56   fn read_community_outbox(
57     conn: &PgConnection,
58     community_actor_id: &DbUrl,
59   ) -> Result<Vec<Value>, Error>;
60 }
61
62 impl Activity_ for Activity {
63   fn insert<T>(
64     conn: &PgConnection,
65     ap_id: DbUrl,
66     data: &T,
67     local: bool,
68     sensitive: bool,
69   ) -> Result<Activity, IoError>
70   where
71     T: Serialize + Debug,
72   {
73     debug!("{}", serde_json::to_string_pretty(&data)?);
74     let activity_form = ActivityForm {
75       ap_id,
76       data: serde_json::to_value(&data)?,
77       local,
78       sensitive,
79       updated: None,
80     };
81     let result = Activity::create(&conn, &activity_form);
82     match result {
83       Ok(s) => Ok(s),
84       Err(e) => Err(IoError::new(
85         ErrorKind::Other,
86         format!("Failed to insert activity into database: {}", e),
87       )),
88     }
89   }
90
91   fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error> {
92     use lemmy_db_schema::schema::activity::dsl::*;
93     activity.filter(ap_id.eq(object_id)).first::<Self>(conn)
94   }
95
96   fn delete_olds(conn: &PgConnection) -> Result<usize, Error> {
97     use lemmy_db_schema::schema::activity::dsl::*;
98     diesel::delete(activity.filter(published.lt(now - 6.months()))).execute(conn)
99   }
100
101   fn read_community_outbox(
102     conn: &PgConnection,
103     community_actor_id: &DbUrl,
104   ) -> Result<Vec<Value>, Error> {
105     use lemmy_db_schema::schema::activity::dsl::*;
106     let res: Vec<Value> = activity
107       .select(data)
108       .filter(
109         sql("activity.data ->> 'type' = 'Announce'")
110           .sql(" AND activity.data -> 'object' ->> 'type' = 'Create'")
111           .sql(" AND activity.data -> 'object' -> 'object' ->> 'type' = 'Page'")
112           .sql(" AND activity.data ->> 'actor' = ")
113           .bind::<Text, _>(community_actor_id)
114           .sql(" ORDER BY activity.published DESC"),
115       )
116       .limit(20)
117       .get_results(conn)?;
118     Ok(res)
119   }
120 }
121
122 #[cfg(test)]
123 mod tests {
124   use super::*;
125   use crate::{
126     establish_unpooled_connection,
127     source::activity::Activity_,
128     Crud,
129     ListingType,
130     SortType,
131   };
132   use lemmy_db_schema::source::{
133     activity::{Activity, ActivityForm},
134     user::{UserForm, User_},
135   };
136   use serde_json::Value;
137   use serial_test::serial;
138   use url::Url;
139
140   #[test]
141   #[serial]
142   fn test_crud() {
143     let conn = establish_unpooled_connection();
144
145     let creator_form = UserForm {
146       name: "activity_creator_pm".into(),
147       preferred_username: None,
148       password_encrypted: "nope".into(),
149       email: None,
150       matrix_user_id: None,
151       avatar: None,
152       banner: None,
153       admin: false,
154       banned: Some(false),
155       published: None,
156       updated: None,
157       show_nsfw: false,
158       theme: "browser".into(),
159       default_sort_type: SortType::Hot as i16,
160       default_listing_type: ListingType::Subscribed as i16,
161       lang: "browser".into(),
162       show_avatars: true,
163       send_notifications_to_email: false,
164       actor_id: None,
165       bio: None,
166       local: true,
167       private_key: None,
168       public_key: None,
169       last_refreshed_at: None,
170       inbox_url: None,
171       shared_inbox_url: None,
172     };
173
174     let inserted_creator = User_::create(&conn, &creator_form).unwrap();
175
176     let ap_id: DbUrl = Url::parse(
177       "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
178     )
179     .unwrap()
180     .into();
181     let test_json: Value = serde_json::from_str(
182       r#"{
183     "@context": "https://www.w3.org/ns/activitystreams",
184     "id": "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
185     "type": "Delete",
186     "actor": "https://enterprise.lemmy.ml/u/riker",
187     "to": "https://www.w3.org/ns/activitystreams#Public",
188     "cc": [
189         "https://enterprise.lemmy.ml/c/main/"
190     ],
191     "object": "https://enterprise.lemmy.ml/post/32"
192     }"#,
193     )
194     .unwrap();
195     let activity_form = ActivityForm {
196       ap_id: ap_id.clone(),
197       data: test_json.to_owned(),
198       local: true,
199       sensitive: false,
200       updated: None,
201     };
202
203     let inserted_activity = Activity::create(&conn, &activity_form).unwrap();
204
205     let expected_activity = Activity {
206       ap_id: Some(ap_id.clone()),
207       id: inserted_activity.id,
208       data: test_json,
209       local: true,
210       sensitive: Some(false),
211       published: inserted_activity.published,
212       updated: None,
213     };
214
215     let read_activity = Activity::read(&conn, inserted_activity.id).unwrap();
216     let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, &ap_id).unwrap();
217     User_::delete(&conn, inserted_creator.id).unwrap();
218     Activity::delete(&conn, inserted_activity.id).unwrap();
219
220     assert_eq!(expected_activity, read_activity);
221     assert_eq!(expected_activity, read_activity_by_apub_id);
222     assert_eq!(expected_activity, inserted_activity);
223   }
224 }