Replace post attachments and other related objects when processing Update(Note) activity
This commit is contained in:
parent
86beb532e2
commit
bc19a524c4
5 changed files with 105 additions and 13 deletions
|
@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Replace post attachments and other related objects when processing `Update(Note)` activity.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Use proof suites with prefix `Mitra`.
|
- Use proof suites with prefix `Mitra`.
|
||||||
|
|
|
@ -131,7 +131,7 @@ fn is_gnu_social_link(author_id: &str, attachment: &Attachment) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_object_attachments(
|
pub async fn get_object_attachments(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
db_client: &impl DatabaseClient,
|
db_client: &impl DatabaseClient,
|
||||||
object: &Object,
|
object: &Object,
|
||||||
|
@ -197,7 +197,7 @@ async fn get_object_attachments(
|
||||||
Ok(attachments)
|
Ok(attachments)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_object_tags(
|
pub async fn get_object_tags(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
db_client: &impl DatabaseClient,
|
db_client: &impl DatabaseClient,
|
||||||
object: &Object,
|
object: &Object,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
@ -7,7 +9,11 @@ use crate::activitypub::{
|
||||||
helpers::update_remote_profile,
|
helpers::update_remote_profile,
|
||||||
types::Actor,
|
types::Actor,
|
||||||
},
|
},
|
||||||
handlers::create::get_object_content,
|
handlers::create::{
|
||||||
|
get_object_attachments,
|
||||||
|
get_object_content,
|
||||||
|
get_object_tags,
|
||||||
|
},
|
||||||
types::Object,
|
types::Object,
|
||||||
vocabulary::{NOTE, PERSON},
|
vocabulary::{NOTE, PERSON},
|
||||||
};
|
};
|
||||||
|
@ -25,24 +31,48 @@ use crate::models::{
|
||||||
use super::HandlerResult;
|
use super::HandlerResult;
|
||||||
|
|
||||||
async fn handle_update_note(
|
async fn handle_update_note(
|
||||||
|
config: &Config,
|
||||||
db_client: &mut impl DatabaseClient,
|
db_client: &mut impl DatabaseClient,
|
||||||
activity: Value,
|
activity: Value,
|
||||||
) -> HandlerResult {
|
) -> HandlerResult {
|
||||||
let object: Object = serde_json::from_value(activity["object"].to_owned())
|
let object: Object = serde_json::from_value(activity["object"].to_owned())
|
||||||
.map_err(|_| ValidationError("invalid object"))?;
|
.map_err(|_| ValidationError("invalid object"))?;
|
||||||
let post_id = match get_post_by_remote_object_id(
|
let post = match get_post_by_remote_object_id(
|
||||||
db_client,
|
db_client,
|
||||||
&object.id,
|
&object.id,
|
||||||
).await {
|
).await {
|
||||||
Ok(post) => post.id,
|
Ok(post) => post,
|
||||||
// Ignore Update if post is not found locally
|
// Ignore Update if post is not found locally
|
||||||
Err(DatabaseError::NotFound(_)) => return Ok(None),
|
Err(DatabaseError::NotFound(_)) => return Ok(None),
|
||||||
Err(other_error) => return Err(other_error.into()),
|
Err(other_error) => return Err(other_error.into()),
|
||||||
};
|
};
|
||||||
let content = get_object_content(&object)?;
|
let content = get_object_content(&object)?;
|
||||||
let updated_at = object.updated.unwrap_or(Utc::now());
|
let updated_at = object.updated.unwrap_or(Utc::now());
|
||||||
let post_data = PostUpdateData { content, updated_at };
|
let attachments = get_object_attachments(
|
||||||
update_post(db_client, &post_id, post_data).await?;
|
config,
|
||||||
|
db_client,
|
||||||
|
&object,
|
||||||
|
&post.author,
|
||||||
|
).await?;
|
||||||
|
if content.is_empty() && attachments.is_empty() {
|
||||||
|
return Err(ValidationError("post is empty").into());
|
||||||
|
};
|
||||||
|
let (mentions, hashtags, links, emojis) = get_object_tags(
|
||||||
|
config,
|
||||||
|
db_client,
|
||||||
|
&object,
|
||||||
|
&HashMap::new(),
|
||||||
|
).await?;
|
||||||
|
let post_data = PostUpdateData {
|
||||||
|
content,
|
||||||
|
attachments,
|
||||||
|
mentions,
|
||||||
|
tags: hashtags,
|
||||||
|
links,
|
||||||
|
emojis,
|
||||||
|
updated_at,
|
||||||
|
};
|
||||||
|
update_post(db_client, &post.id, post_data).await?;
|
||||||
Ok(Some(NOTE))
|
Ok(Some(NOTE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +115,7 @@ pub async fn handle_update(
|
||||||
.ok_or(ValidationError("unknown object type"))?;
|
.ok_or(ValidationError("unknown object type"))?;
|
||||||
match object_type {
|
match object_type {
|
||||||
NOTE => {
|
NOTE => {
|
||||||
handle_update_note(db_client, activity).await
|
handle_update_note(config, db_client, activity).await
|
||||||
},
|
},
|
||||||
PERSON => {
|
PERSON => {
|
||||||
handle_update_person(config, db_client, activity).await
|
handle_update_person(config, db_client, activity).await
|
||||||
|
|
|
@ -306,12 +306,13 @@ pub async fn create_post(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_post(
|
pub async fn update_post(
|
||||||
db_client: &impl DatabaseClient,
|
db_client: &mut impl DatabaseClient,
|
||||||
post_id: &Uuid,
|
post_id: &Uuid,
|
||||||
post_data: PostUpdateData,
|
post_data: PostUpdateData,
|
||||||
) -> Result<(), DatabaseError> {
|
) -> Result<(), DatabaseError> {
|
||||||
|
let transaction = db_client.transaction().await?;
|
||||||
// Reposts and immutable posts can't be updated
|
// Reposts and immutable posts can't be updated
|
||||||
let updated_count = db_client.execute(
|
let maybe_row = transaction.query_opt(
|
||||||
"
|
"
|
||||||
UPDATE post
|
UPDATE post
|
||||||
SET
|
SET
|
||||||
|
@ -320,6 +321,7 @@ pub async fn update_post(
|
||||||
WHERE id = $3
|
WHERE id = $3
|
||||||
AND repost_of_id IS NULL
|
AND repost_of_id IS NULL
|
||||||
AND ipfs_cid IS NULL
|
AND ipfs_cid IS NULL
|
||||||
|
RETURNING post
|
||||||
",
|
",
|
||||||
&[
|
&[
|
||||||
&post_data.content,
|
&post_data.content,
|
||||||
|
@ -327,9 +329,58 @@ pub async fn update_post(
|
||||||
&post_id,
|
&post_id,
|
||||||
],
|
],
|
||||||
).await?;
|
).await?;
|
||||||
if updated_count == 0 {
|
let row = maybe_row.ok_or(DatabaseError::NotFound("post"))?;
|
||||||
return Err(DatabaseError::NotFound("post"));
|
let db_post: DbPost = row.try_get("post")?;
|
||||||
};
|
|
||||||
|
// Delete and re-create related objects
|
||||||
|
transaction.execute(
|
||||||
|
"DELETE FROM media_attachment WHERE post_id = $1",
|
||||||
|
&[&db_post.id],
|
||||||
|
).await?;
|
||||||
|
transaction.execute(
|
||||||
|
"DELETE FROM mention WHERE post_id = $1",
|
||||||
|
&[&db_post.id],
|
||||||
|
).await?;
|
||||||
|
transaction.execute(
|
||||||
|
"DELETE FROM post_tag WHERE post_id = $1",
|
||||||
|
&[&db_post.id],
|
||||||
|
).await?;
|
||||||
|
transaction.execute(
|
||||||
|
"DELETE FROM post_link WHERE source_id = $1",
|
||||||
|
&[&db_post.id],
|
||||||
|
).await?;
|
||||||
|
transaction.execute(
|
||||||
|
"DELETE FROM post_emoji WHERE post_id = $1",
|
||||||
|
&[&db_post.id],
|
||||||
|
).await?;
|
||||||
|
create_post_attachments(
|
||||||
|
&transaction,
|
||||||
|
&db_post.id,
|
||||||
|
&db_post.author_id,
|
||||||
|
post_data.attachments,
|
||||||
|
).await?;
|
||||||
|
create_post_mentions(
|
||||||
|
&transaction,
|
||||||
|
&db_post.id,
|
||||||
|
post_data.mentions,
|
||||||
|
).await?;
|
||||||
|
create_post_tags(
|
||||||
|
&transaction,
|
||||||
|
&db_post.id,
|
||||||
|
post_data.tags,
|
||||||
|
).await?;
|
||||||
|
create_post_links(
|
||||||
|
&transaction,
|
||||||
|
&db_post.id,
|
||||||
|
post_data.links,
|
||||||
|
).await?;
|
||||||
|
create_post_emojis(
|
||||||
|
&transaction,
|
||||||
|
&db_post.id,
|
||||||
|
post_data.emojis,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
transaction.commit().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1323,6 +1374,7 @@ mod tests {
|
||||||
let post_data = PostUpdateData {
|
let post_data = PostUpdateData {
|
||||||
content: "test update".to_string(),
|
content: "test update".to_string(),
|
||||||
updated_at: Utc::now(),
|
updated_at: Utc::now(),
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
update_post(db_client, &post.id, post_data).await.unwrap();
|
update_post(db_client, &post.id, post_data).await.unwrap();
|
||||||
let post = get_post_by_id(db_client, &post.id).await.unwrap();
|
let post = get_post_by_id(db_client, &post.id).await.unwrap();
|
||||||
|
|
|
@ -276,7 +276,13 @@ impl PostCreateData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Default))]
|
||||||
pub struct PostUpdateData {
|
pub struct PostUpdateData {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
|
pub attachments: Vec<Uuid>,
|
||||||
|
pub mentions: Vec<Uuid>,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
pub links: Vec<Uuid>,
|
||||||
|
pub emojis: Vec<Uuid>,
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue