]> Untitled Git - lemmy.git/blob - crates/db_queries/src/source/activity.rs
Rewrite delete activities (#1699)
[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 for Activity {
13   type Form = ActivityForm;
14   type IdType = i32;
15   fn read(conn: &PgConnection, activity_id: i32) -> Result<Self, Error> {
16     use lemmy_db_schema::schema::activity::dsl::*;
17     activity.find(activity_id).first::<Self>(conn)
18   }
19
20   fn create(conn: &PgConnection, new_activity: &ActivityForm) -> Result<Self, Error> {
21     use lemmy_db_schema::schema::activity::dsl::*;
22     insert_into(activity)
23       .values(new_activity)
24       .get_result::<Self>(conn)
25   }
26
27   fn update(
28     conn: &PgConnection,
29     activity_id: i32,
30     new_activity: &ActivityForm,
31   ) -> Result<Self, Error> {
32     use lemmy_db_schema::schema::activity::dsl::*;
33     diesel::update(activity.find(activity_id))
34       .set(new_activity)
35       .get_result::<Self>(conn)
36   }
37   fn delete(conn: &PgConnection, activity_id: i32) -> Result<usize, Error> {
38     use lemmy_db_schema::schema::activity::dsl::*;
39     diesel::delete(activity.find(activity_id)).execute(conn)
40   }
41 }
42
43 pub trait Activity_ {
44   fn insert<T>(
45     conn: &PgConnection,
46     ap_id: DbUrl,
47     data: &T,
48     local: bool,
49     sensitive: bool,
50   ) -> Result<Activity, IoError>
51   where
52     T: Serialize + Debug;
53
54   fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error>;
55   fn delete_olds(conn: &PgConnection) -> Result<usize, Error>;
56
57   /// Returns up to 20 activities of type `Announce/Create/Page` from the community
58   fn read_community_outbox(
59     conn: &PgConnection,
60     community_actor_id: &DbUrl,
61   ) -> Result<Vec<Value>, Error>;
62 }
63
64 impl Activity_ for Activity {
65   fn insert<T>(
66     conn: &PgConnection,
67     ap_id: DbUrl,
68     data: &T,
69     local: bool,
70     sensitive: bool,
71   ) -> Result<Activity, IoError>
72   where
73     T: Serialize + Debug,
74   {
75     debug!("{}", serde_json::to_string_pretty(&data)?);
76     let activity_form = ActivityForm {
77       ap_id,
78       data: serde_json::to_value(&data)?,
79       local: Some(local),
80       sensitive,
81       updated: None,
82     };
83     let result = Activity::create(conn, &activity_form);
84     match result {
85       Ok(s) => Ok(s),
86       Err(e) => Err(IoError::new(
87         ErrorKind::Other,
88         format!("Failed to insert activity into database: {}", e),
89       )),
90     }
91   }
92
93   fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error> {
94     use lemmy_db_schema::schema::activity::dsl::*;
95     activity.filter(ap_id.eq(object_id)).first::<Self>(conn)
96   }
97
98   fn delete_olds(conn: &PgConnection) -> Result<usize, Error> {
99     use lemmy_db_schema::schema::activity::dsl::*;
100     diesel::delete(activity.filter(published.lt(now - 6.months()))).execute(conn)
101   }
102
103   fn read_community_outbox(
104     conn: &PgConnection,
105     community_actor_id: &DbUrl,
106   ) -> Result<Vec<Value>, Error> {
107     use lemmy_db_schema::schema::activity::dsl::*;
108     let res: Vec<Value> = activity
109       .select(data)
110       .filter(
111         sql("activity.data ->> 'type' = 'Announce'")
112           .sql(" AND activity.data -> 'object' ->> 'type' = 'Create'")
113           .sql(" AND activity.data -> 'object' -> 'object' ->> 'type' = 'Page'")
114           .sql(" AND activity.data ->> 'actor' = ")
115           .bind::<Text, _>(community_actor_id)
116           .sql(" ORDER BY activity.published DESC"),
117       )
118       .limit(20)
119       .get_results(conn)?;
120     Ok(res)
121   }
122 }
123
124 #[cfg(test)]
125 mod tests {
126   use super::*;
127   use crate::{establish_unpooled_connection, source::activity::Activity_};
128   use lemmy_db_schema::source::{
129     activity::{Activity, ActivityForm},
130     person::{Person, PersonForm},
131   };
132   use serde_json::Value;
133   use serial_test::serial;
134   use url::Url;
135
136   #[test]
137   #[serial]
138   fn test_crud() {
139     let conn = establish_unpooled_connection();
140
141     let creator_form = PersonForm {
142       name: "activity_creator_pm".into(),
143       ..PersonForm::default()
144     };
145
146     let inserted_creator = Person::create(&conn, &creator_form).unwrap();
147
148     let ap_id: DbUrl = Url::parse(
149       "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
150     )
151     .unwrap()
152     .into();
153     let test_json: Value = serde_json::from_str(
154       r#"{
155     "@context": "https://www.w3.org/ns/activitystreams",
156     "id": "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
157     "type": "Delete",
158     "actor": "https://enterprise.lemmy.ml/u/riker",
159     "to": "https://www.w3.org/ns/activitystreams#Public",
160     "cc": [
161         "https://enterprise.lemmy.ml/c/main/"
162     ],
163     "object": "https://enterprise.lemmy.ml/post/32"
164     }"#,
165     )
166     .unwrap();
167     let activity_form = ActivityForm {
168       ap_id: ap_id.clone(),
169       data: test_json.to_owned(),
170       local: Some(true),
171       sensitive: false,
172       updated: None,
173     };
174
175     let inserted_activity = Activity::create(&conn, &activity_form).unwrap();
176
177     let expected_activity = Activity {
178       ap_id: Some(ap_id.clone()),
179       id: inserted_activity.id,
180       data: test_json,
181       local: true,
182       sensitive: Some(false),
183       published: inserted_activity.published,
184       updated: None,
185     };
186
187     let read_activity = Activity::read(&conn, inserted_activity.id).unwrap();
188     let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, &ap_id).unwrap();
189     Person::delete(&conn, inserted_creator.id).unwrap();
190     Activity::delete(&conn, inserted_activity.id).unwrap();
191
192     assert_eq!(expected_activity, read_activity);
193     assert_eq!(expected_activity, read_activity_by_apub_id);
194     assert_eq!(expected_activity, inserted_activity);
195   }
196 }