2 newtypes::{CommentId, DbUrl, PersonId},
11 traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable},
14 use diesel::{dsl::*, result::Error, *};
15 use diesel_ltree::Ltree;
21 comment_id: CommentId,
23 ) -> Result<Self, Error> {
24 use crate::schema::comment::dsl::*;
26 diesel::update(comment.find(comment_id))
27 .set(ap_id.eq(apub_id))
28 .get_result::<Self>(conn)
31 pub fn permadelete_for_creator(
33 for_creator_id: PersonId,
34 ) -> Result<Vec<Self>, Error> {
35 use crate::schema::comment::dsl::*;
36 diesel::update(comment.filter(creator_id.eq(for_creator_id)))
38 content.eq("*Permananently Deleted*"),
40 updated.eq(naive_now()),
42 .get_results::<Self>(conn)
45 pub fn update_deleted(
47 comment_id: CommentId,
49 ) -> Result<Self, Error> {
50 use crate::schema::comment::dsl::*;
51 diesel::update(comment.find(comment_id))
52 .set((deleted.eq(new_deleted), updated.eq(naive_now())))
53 .get_result::<Self>(conn)
56 pub fn update_removed(
58 comment_id: CommentId,
60 ) -> Result<Self, Error> {
61 use crate::schema::comment::dsl::*;
62 diesel::update(comment.find(comment_id))
63 .set((removed.eq(new_removed), updated.eq(naive_now())))
64 .get_result::<Self>(conn)
67 pub fn update_removed_for_creator(
69 for_creator_id: PersonId,
71 ) -> Result<Vec<Self>, Error> {
72 use crate::schema::comment::dsl::*;
73 diesel::update(comment.filter(creator_id.eq(for_creator_id)))
74 .set((removed.eq(new_removed), updated.eq(naive_now())))
75 .get_results::<Self>(conn)
78 pub fn update_content(
80 comment_id: CommentId,
82 ) -> Result<Self, Error> {
83 use crate::schema::comment::dsl::*;
84 diesel::update(comment.find(comment_id))
85 .set((content.eq(new_content), updated.eq(naive_now())))
86 .get_result::<Self>(conn)
89 pub fn update_distinguished(
91 comment_id: CommentId,
92 new_distinguished: bool,
93 ) -> Result<Self, Error> {
94 use crate::schema::comment::dsl::*;
95 diesel::update(comment.find(comment_id))
96 .set((distinguished.eq(new_distinguished), updated.eq(naive_now())))
97 .get_result::<Self>(conn)
102 comment_form: &CommentForm,
103 parent_path: Option<&Ltree>,
104 ) -> Result<Comment, Error> {
105 use crate::schema::comment::dsl::*;
107 // Insert, to get the id
108 let inserted_comment = insert_into(comment)
109 .values(comment_form)
113 .get_result::<Self>(conn);
115 if let Ok(comment_insert) = inserted_comment {
116 let comment_id = comment_insert.id;
118 // You need to update the ltree column
119 let ltree = Ltree(if let Some(parent_path) = parent_path {
120 // The previous parent will already have 0 in it
121 // Append this comment id
122 format!("{}.{}", parent_path.0, comment_id)
124 // '0' is always the first path, append to that
125 format!("{}.{}", 0, comment_id)
128 let updated_comment = diesel::update(comment.find(comment_id))
130 .get_result::<Self>(conn);
132 // Update the child count for the parent comment_aggregates
133 // You could do this with a trigger, but since you have to do this manually anyway,
134 // you can just have it here
135 if let Some(parent_path) = parent_path {
136 // You have to update counts for all parents, not just the immediate one
137 // TODO if the performance of this is terrible, it might be better to do this as part of a
138 // scheduled query... although the counts would often be wrong.
140 // The child_count query for reference:
141 // select c.id, c.path, count(c2.id) as child_count from comment c
142 // left join comment c2 on c2.path <@ c.path and c2.path != c.path
145 let top_parent = format!("0.{}", parent_path.0.split('.').collect::<Vec<&str>>()[1]);
146 let update_child_count_stmt = format!(
148 update comment_aggregates ca set child_count = c.child_count
150 select c.id, c.path, count(c2.id) as child_count from comment c
151 join comment c2 on c2.path <@ c.path and c2.path != c.path
155 where ca.comment_id = c.id",
159 sql_query(update_child_count_stmt).execute(conn)?;
166 pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
167 use crate::schema::comment::dsl::*;
168 let object_id: DbUrl = object_id.into();
171 .filter(ap_id.eq(object_id))
172 .first::<Comment>(conn)
178 pub fn parent_comment_id(&self) -> Option<CommentId> {
179 let mut ltree_split: Vec<&str> = self.path.0.split('.').collect();
180 ltree_split.remove(0); // The first is always 0
181 if ltree_split.len() > 1 {
182 ltree_split[ltree_split.len() - 2]
192 impl Crud for Comment {
193 type Form = CommentForm;
194 type IdType = CommentId;
195 fn read(conn: &PgConnection, comment_id: CommentId) -> Result<Self, Error> {
196 use crate::schema::comment::dsl::*;
197 comment.find(comment_id).first::<Self>(conn)
200 fn delete(conn: &PgConnection, comment_id: CommentId) -> Result<usize, Error> {
201 use crate::schema::comment::dsl::*;
202 diesel::delete(comment.find(comment_id)).execute(conn)
205 /// This is unimplemented, use [[Comment::create]]
206 fn create(_conn: &PgConnection, _comment_form: &CommentForm) -> Result<Self, Error> {
212 comment_id: CommentId,
213 comment_form: &CommentForm,
214 ) -> Result<Self, Error> {
215 use crate::schema::comment::dsl::*;
216 diesel::update(comment.find(comment_id))
218 .get_result::<Self>(conn)
222 impl Likeable for CommentLike {
223 type Form = CommentLikeForm;
224 type IdType = CommentId;
225 fn like(conn: &PgConnection, comment_like_form: &CommentLikeForm) -> Result<Self, Error> {
226 use crate::schema::comment_like::dsl::*;
227 insert_into(comment_like)
228 .values(comment_like_form)
229 .on_conflict((comment_id, person_id))
231 .set(comment_like_form)
232 .get_result::<Self>(conn)
237 comment_id: CommentId,
238 ) -> Result<usize, Error> {
239 use crate::schema::comment_like::dsl;
242 .filter(dsl::comment_id.eq(comment_id))
243 .filter(dsl::person_id.eq(person_id)),
249 impl Saveable for CommentSaved {
250 type Form = CommentSavedForm;
251 fn save(conn: &PgConnection, comment_saved_form: &CommentSavedForm) -> Result<Self, Error> {
252 use crate::schema::comment_saved::dsl::*;
253 insert_into(comment_saved)
254 .values(comment_saved_form)
255 .on_conflict((comment_id, person_id))
257 .set(comment_saved_form)
258 .get_result::<Self>(conn)
260 fn unsave(conn: &PgConnection, comment_saved_form: &CommentSavedForm) -> Result<usize, Error> {
261 use crate::schema::comment_saved::dsl::*;
264 .filter(comment_id.eq(comment_saved_form.comment_id))
265 .filter(person_id.eq(comment_saved_form.person_id)),
271 impl DeleteableOrRemoveable for Comment {
272 fn blank_out_deleted_or_removed_info(mut self) -> Self {
273 self.content = "".into();
283 community::{Community, CommunityForm},
284 person::{Person, PersonForm},
287 traits::{Crud, Likeable, Saveable},
288 utils::establish_unpooled_connection,
290 use diesel_ltree::Ltree;
291 use serial_test::serial;
296 let conn = establish_unpooled_connection();
298 let new_person = PersonForm {
299 name: "terry".into(),
300 public_key: Some("pubkey".to_string()),
301 ..PersonForm::default()
304 let inserted_person = Person::create(&conn, &new_person).unwrap();
306 let new_community = CommunityForm {
307 name: "test community".to_string(),
308 title: "nada".to_owned(),
309 public_key: Some("pubkey".to_string()),
310 ..CommunityForm::default()
313 let inserted_community = Community::create(&conn, &new_community).unwrap();
315 let new_post = PostForm {
316 name: "A test post".into(),
317 creator_id: inserted_person.id,
318 community_id: inserted_community.id,
319 ..PostForm::default()
322 let inserted_post = Post::create(&conn, &new_post).unwrap();
324 let comment_form = CommentForm {
325 content: "A test comment".into(),
326 creator_id: inserted_person.id,
327 post_id: inserted_post.id,
328 ..CommentForm::default()
331 let inserted_comment = Comment::create(&conn, &comment_form, None).unwrap();
333 let expected_comment = Comment {
334 id: inserted_comment.id,
335 content: "A test comment".into(),
336 creator_id: inserted_person.id,
337 post_id: inserted_post.id,
340 path: Ltree(format!("0.{}", inserted_comment.id)),
341 published: inserted_comment.published,
343 ap_id: inserted_comment.ap_id.to_owned(),
344 distinguished: false,
348 let child_comment_form = CommentForm {
349 content: "A child comment".into(),
350 creator_id: inserted_person.id,
351 post_id: inserted_post.id,
352 // path: Some(text2ltree(inserted_comment.id),
353 ..CommentForm::default()
356 let inserted_child_comment =
357 Comment::create(&conn, &child_comment_form, Some(&inserted_comment.path)).unwrap();
360 let comment_like_form = CommentLikeForm {
361 comment_id: inserted_comment.id,
362 post_id: inserted_post.id,
363 person_id: inserted_person.id,
367 let inserted_comment_like = CommentLike::like(&conn, &comment_like_form).unwrap();
369 let expected_comment_like = CommentLike {
370 id: inserted_comment_like.id,
371 comment_id: inserted_comment.id,
372 post_id: inserted_post.id,
373 person_id: inserted_person.id,
374 published: inserted_comment_like.published,
379 let comment_saved_form = CommentSavedForm {
380 comment_id: inserted_comment.id,
381 person_id: inserted_person.id,
384 let inserted_comment_saved = CommentSaved::save(&conn, &comment_saved_form).unwrap();
386 let expected_comment_saved = CommentSaved {
387 id: inserted_comment_saved.id,
388 comment_id: inserted_comment.id,
389 person_id: inserted_person.id,
390 published: inserted_comment_saved.published,
393 let read_comment = Comment::read(&conn, inserted_comment.id).unwrap();
394 let updated_comment = Comment::update(&conn, inserted_comment.id, &comment_form).unwrap();
395 let like_removed = CommentLike::remove(&conn, inserted_person.id, inserted_comment.id).unwrap();
396 let saved_removed = CommentSaved::unsave(&conn, &comment_saved_form).unwrap();
397 let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
398 Comment::delete(&conn, inserted_child_comment.id).unwrap();
399 Post::delete(&conn, inserted_post.id).unwrap();
400 Community::delete(&conn, inserted_community.id).unwrap();
401 Person::delete(&conn, inserted_person.id).unwrap();
403 assert_eq!(expected_comment, read_comment);
404 assert_eq!(expected_comment, inserted_comment);
405 assert_eq!(expected_comment, updated_comment);
406 assert_eq!(expected_comment_like, inserted_comment_like);
407 assert_eq!(expected_comment_saved, inserted_comment_saved);
409 format!("0.{}.{}", expected_comment.id, inserted_child_comment.id),
410 inserted_child_comment.path.0,
412 assert_eq!(1, like_removed);
413 assert_eq!(1, saved_removed);
414 assert_eq!(1, num_deleted);