From b2937223dfea9332f87f91bbbcc4780d6bfa4a7c Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 5 Aug 2021 13:00:29 +0200 Subject: [PATCH] Migrate comment inReplyTo field to single value (ref #1454) --- crates/apub/src/lib.rs | 1 + crates/apub/src/migrations.rs | 17 +++++++ crates/apub/src/objects/comment.rs | 76 +++++++++++++++++++++--------- 3 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 crates/apub/src/migrations.rs diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 60d7c4871..75f97aadd 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -6,6 +6,7 @@ pub mod activity_queue; pub mod extensions; pub mod fetcher; pub mod http; +pub mod migrations; pub mod objects; use crate::{ diff --git a/crates/apub/src/migrations.rs b/crates/apub/src/migrations.rs new file mode 100644 index 000000000..0f18d2f37 --- /dev/null +++ b/crates/apub/src/migrations.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; +use url::Url; + +/// Migrate comment.in_reply_to field from containing post and parent comment ID, to only containing +/// the direct parent (whether its a post or comment). This is for compatibility with Pleroma and +/// Smithereen. +/// [https://github.com/LemmyNet/lemmy/issues/1454] +/// +/// v0.12: receive both, send old (compatible with v0.11) +/// v0.13 receive both, send new (compatible with v0.12+, incompatible with v0.11) +/// v0.14: only send and receive new, remove migration (compatible with v0.13+) +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +pub enum CommentInReplyToMigration { + Old(Vec), + New(Url), +} diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index c97ea994c..d402613a0 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -1,9 +1,15 @@ use crate::{ activities::verify_person_in_community, extensions::context::lemmy_context, - fetcher::objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post}, + fetcher::objects::{ + get_or_fetch_and_insert_comment, + get_or_fetch_and_insert_post, + get_or_fetch_and_insert_post_or_comment, + }, + migrations::CommentInReplyToMigration, objects::{create_tombstone, get_or_fetch_and_upsert_person, FromApub, Source, ToApub}, ActorType, + PostOrComment, }; use activitystreams::{ base::AnyBase, @@ -35,6 +41,7 @@ use lemmy_utils::{ }; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; +use std::ops::Deref; use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] @@ -52,7 +59,7 @@ pub struct Note { content: String, media_type: MediaTypeHtml, source: Source, - in_reply_to: Vec, + in_reply_to: CommentInReplyToMigration, published: DateTime, updated: Option>, #[serde(flatten)] @@ -65,32 +72,55 @@ impl Note { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(Post, Option), LemmyError> { - // This post, or the parent comment might not yet exist on this server yet, fetch them. - let post_id = self.in_reply_to.get(0).context(location_info!())?; - let post = Box::pin(get_or_fetch_and_insert_post( - post_id, - context, - request_counter, - )) - .await?; - - // The 2nd item, if it exists, is the parent comment apub_id - // Nested comments will automatically get fetched recursively - let parent_id: Option = match self.in_reply_to.get(1) { - Some(parent_comment_uri) => { - let parent_comment = Box::pin(get_or_fetch_and_insert_comment( - parent_comment_uri, + match &self.in_reply_to { + CommentInReplyToMigration::Old(in_reply_to) => { + // This post, or the parent comment might not yet exist on this server yet, fetch them. + let post_id = in_reply_to.get(0).context(location_info!())?; + let post = Box::pin(get_or_fetch_and_insert_post( + post_id, context, request_counter, )) .await?; - Some(parent_comment.id) - } - None => None, - }; + // The 2nd item, if it exists, is the parent comment apub_id + // Nested comments will automatically get fetched recursively + let parent_id: Option = match in_reply_to.get(1) { + Some(parent_comment_uri) => { + let parent_comment = Box::pin(get_or_fetch_and_insert_comment( + parent_comment_uri, + context, + request_counter, + )) + .await?; - Ok((post, parent_id)) + Some(parent_comment.id) + } + None => None, + }; + + Ok((post, parent_id)) + } + CommentInReplyToMigration::New(in_reply_to) => { + let parent = Box::pin( + get_or_fetch_and_insert_post_or_comment(in_reply_to, context, request_counter).await?, + ); + match parent.deref() { + PostOrComment::Post(p) => { + // Workaround because I cant figure ut how to get the post out of the box (and we dont + // want to stackoverflow in a deep comment hierarchy). + let post_id = p.id; + let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; + Ok((post, None)) + } + PostOrComment::Comment(c) => { + let post_id = c.post_id; + let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; + Ok((post, Some(c.id))) + } + } + } + } } pub(crate) async fn verify( @@ -153,7 +183,7 @@ impl ToApub for Comment { content: self.content.clone(), media_type: MediaTypeMarkdown::Markdown, }, - in_reply_to: in_reply_to_vec, + in_reply_to: CommentInReplyToMigration::Old(in_reply_to_vec), published: convert_datetime(self.published), updated: self.updated.map(convert_datetime), unparsed: Default::default(),