]> Untitled Git - lemmy.git/blob - crates/db_schema/src/impls/activity.rs
Merge pull request #1850 from LemmyNet/refactor-apub
[lemmy.git] / crates / db_schema / src / impls / activity.rs
1 use crate::{newtypes::DbUrl, source::activity::*, traits::Crud};
2 use diesel::{dsl::*, result::Error, sql_types::Text, *};
3 use serde::Serialize;
4 use serde_json::Value;
5 use std::{
6   fmt::Debug,
7   io::{Error as IoError, ErrorKind},
8 };
9
10 impl Crud for Activity {
11   type Form = ActivityForm;
12   type IdType = i32;
13   fn read(conn: &PgConnection, activity_id: i32) -> Result<Self, Error> {
14     use crate::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 crate::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 crate::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 crate::schema::activity::dsl::*;
37     diesel::delete(activity.find(activity_id)).execute(conn)
38   }
39 }
40
41 impl Activity {
42   pub 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     let activity_form = ActivityForm {
53       ap_id,
54       data: serde_json::to_value(&data)?,
55       local: Some(local),
56       sensitive,
57       updated: None,
58     };
59     let result = Activity::create(conn, &activity_form);
60     match result {
61       Ok(s) => Ok(s),
62       Err(e) => Err(IoError::new(
63         ErrorKind::Other,
64         format!("Failed to insert activity into database: {}", e),
65       )),
66     }
67   }
68
69   pub fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error> {
70     use crate::schema::activity::dsl::*;
71     activity.filter(ap_id.eq(object_id)).first::<Self>(conn)
72   }
73
74   pub fn delete_olds(conn: &PgConnection) -> Result<usize, Error> {
75     use crate::schema::activity::dsl::*;
76     diesel::delete(activity.filter(published.lt(now - 6.months()))).execute(conn)
77   }
78
79   pub fn read_community_outbox(
80     conn: &PgConnection,
81     community_actor_id: &DbUrl,
82   ) -> Result<Vec<Value>, Error> {
83     use crate::schema::activity::dsl::*;
84     let res: Vec<Value> = activity
85       .select(data)
86       .filter(
87         sql("activity.data ->> 'type' = 'Announce'")
88           .sql(" AND activity.data -> 'object' ->> 'type' = 'Create'")
89           .sql(" AND activity.data -> 'object' -> 'object' ->> 'type' = 'Page'")
90           .sql(" AND activity.data ->> 'actor' = ")
91           .bind::<Text, _>(community_actor_id)
92           .sql(" ORDER BY activity.published DESC"),
93       )
94       .limit(20)
95       .get_results(conn)?;
96     Ok(res)
97   }
98 }
99
100 #[cfg(test)]
101 mod tests {
102   use super::*;
103   use crate::{
104     establish_unpooled_connection,
105     newtypes::DbUrl,
106     source::{
107       activity::{Activity, ActivityForm},
108       person::{Person, PersonForm},
109     },
110   };
111   use serde_json::Value;
112   use serial_test::serial;
113   use url::Url;
114
115   #[test]
116   #[serial]
117   fn test_crud() {
118     let conn = establish_unpooled_connection();
119
120     let creator_form = PersonForm {
121       name: "activity_creator_pm".into(),
122       ..PersonForm::default()
123     };
124
125     let inserted_creator = Person::create(&conn, &creator_form).unwrap();
126
127     let ap_id: DbUrl = Url::parse(
128       "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
129     )
130     .unwrap()
131     .into();
132     let test_json: Value = serde_json::from_str(
133       r#"{
134     "@context": "https://www.w3.org/ns/activitystreams",
135     "id": "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
136     "type": "Delete",
137     "actor": "https://enterprise.lemmy.ml/u/riker",
138     "to": "https://www.w3.org/ns/activitystreams#Public",
139     "cc": [
140         "https://enterprise.lemmy.ml/c/main/"
141     ],
142     "object": "https://enterprise.lemmy.ml/post/32"
143     }"#,
144     )
145     .unwrap();
146     let activity_form = ActivityForm {
147       ap_id: ap_id.clone(),
148       data: test_json.to_owned(),
149       local: Some(true),
150       sensitive: false,
151       updated: None,
152     };
153
154     let inserted_activity = Activity::create(&conn, &activity_form).unwrap();
155
156     let expected_activity = Activity {
157       ap_id: Some(ap_id.clone()),
158       id: inserted_activity.id,
159       data: test_json,
160       local: true,
161       sensitive: Some(false),
162       published: inserted_activity.published,
163       updated: None,
164     };
165
166     let read_activity = Activity::read(&conn, inserted_activity.id).unwrap();
167     let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, &ap_id).unwrap();
168     Person::delete(&conn, inserted_creator.id).unwrap();
169     Activity::delete(&conn, inserted_activity.id).unwrap();
170
171     assert_eq!(expected_activity, read_activity);
172     assert_eq!(expected_activity, read_activity_by_apub_id);
173     assert_eq!(expected_activity, inserted_activity);
174   }
175 }