Fetch ancestors of incoming Notes by going through inReplyTo references
This commit is contained in:
parent
3aa35271a6
commit
cca8a5ff94
3 changed files with 94 additions and 5 deletions
|
@ -5,6 +5,7 @@ use serde_json::Value;
|
||||||
use crate::models::profiles::types::ProfileCreateData;
|
use crate::models::profiles::types::ProfileCreateData;
|
||||||
use crate::utils::files::{save_file, FileError};
|
use crate::utils::files::{save_file, FileError};
|
||||||
use crate::webfinger::types::JsonResourceDescriptor;
|
use crate::webfinger::types::JsonResourceDescriptor;
|
||||||
|
use super::activity::Object;
|
||||||
use super::actor::Actor;
|
use super::actor::Actor;
|
||||||
use super::constants::ACTIVITY_CONTENT_TYPE;
|
use super::constants::ACTIVITY_CONTENT_TYPE;
|
||||||
|
|
||||||
|
@ -120,3 +121,16 @@ pub async fn fetch_attachment(
|
||||||
let file_name = save_file(file_data.to_vec(), output_dir)?;
|
let file_name = save_file(file_data.to_vec(), output_dir)?;
|
||||||
Ok(file_name)
|
Ok(file_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_object(
|
||||||
|
object_url: &str,
|
||||||
|
) -> Result<Object, FetchError> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let object_json = client.get(object_url)
|
||||||
|
.header(reqwest::header::ACCEPT, ACTIVITY_CONTENT_TYPE)
|
||||||
|
.send().await?
|
||||||
|
.text().await?;
|
||||||
|
let object_value: Value = serde_json::from_str(&object_json)?;
|
||||||
|
let object: Object = serde_json::from_value(object_value)?;
|
||||||
|
Ok(object)
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@ use crate::database::{Pool, get_database_client};
|
||||||
use crate::errors::{DatabaseError, HttpError, ValidationError};
|
use crate::errors::{DatabaseError, HttpError, ValidationError};
|
||||||
use crate::models::attachments::queries::create_attachment;
|
use crate::models::attachments::queries::create_attachment;
|
||||||
use crate::models::posts::types::PostCreateData;
|
use crate::models::posts::types::PostCreateData;
|
||||||
use crate::models::posts::queries::create_post;
|
use crate::models::posts::queries::{
|
||||||
|
create_post,
|
||||||
|
get_post_by_id,
|
||||||
|
get_post_by_object_id,
|
||||||
|
};
|
||||||
use crate::models::profiles::queries::{
|
use crate::models::profiles::queries::{
|
||||||
get_profile_by_actor_id,
|
get_profile_by_actor_id,
|
||||||
get_profile_by_acct,
|
get_profile_by_acct,
|
||||||
|
@ -32,6 +36,7 @@ use super::fetcher::{
|
||||||
fetch_avatar_and_banner,
|
fetch_avatar_and_banner,
|
||||||
fetch_profile_by_actor_id,
|
fetch_profile_by_actor_id,
|
||||||
fetch_attachment,
|
fetch_attachment,
|
||||||
|
fetch_object,
|
||||||
};
|
};
|
||||||
use super::vocabulary::*;
|
use super::vocabulary::*;
|
||||||
|
|
||||||
|
@ -121,8 +126,40 @@ pub async fn receive_activity(
|
||||||
(CREATE, NOTE) => {
|
(CREATE, NOTE) => {
|
||||||
let object: Object = serde_json::from_value(activity.object)
|
let object: Object = serde_json::from_value(activity.object)
|
||||||
.map_err(|_| ValidationError("invalid object"))?;
|
.map_err(|_| ValidationError("invalid object"))?;
|
||||||
// TOOD: fetch the whole thread
|
let mut maybe_parent_object_id = object.in_reply_to.clone();
|
||||||
let objects = vec![object];
|
let mut objects = vec![object];
|
||||||
|
// Fetch ancestors by going through inReplyTo references
|
||||||
|
// TODO: fetch replies too
|
||||||
|
loop {
|
||||||
|
let object_id = match maybe_parent_object_id {
|
||||||
|
Some(parent_object_id) => {
|
||||||
|
if parse_object_id(&config.instance_url(), &parent_object_id).is_ok() {
|
||||||
|
// Parent object is a local post
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
match get_post_by_object_id(db_client, &parent_object_id).await {
|
||||||
|
Ok(_) => {
|
||||||
|
// Parent object has been fetched already
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Err(DatabaseError::NotFound(_)) => (),
|
||||||
|
Err(other_error) => return Err(other_error.into()),
|
||||||
|
};
|
||||||
|
parent_object_id
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Object does not have a parent
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let object = fetch_object(&object_id).await
|
||||||
|
.map_err(|_| ValidationError("failed to fetch object"))?;
|
||||||
|
maybe_parent_object_id = object.in_reply_to.clone();
|
||||||
|
objects.push(object);
|
||||||
|
}
|
||||||
|
// Objects are ordered according to their place in reply tree,
|
||||||
|
// starting with the root
|
||||||
|
objects.reverse();
|
||||||
for object in objects {
|
for object in objects {
|
||||||
let attributed_to = object.attributed_to
|
let attributed_to = object.attributed_to
|
||||||
.ok_or(ValidationError("unattributed note"))?;
|
.ok_or(ValidationError("unattributed note"))?;
|
||||||
|
@ -153,10 +190,25 @@ pub async fn receive_activity(
|
||||||
attachments.push(db_attachment.id);
|
attachments.push(db_attachment.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let in_reply_to_id = match object.in_reply_to {
|
||||||
|
Some(object_id) => {
|
||||||
|
match parse_object_id(&config.instance_url(), &object_id) {
|
||||||
|
Ok(post_id) => {
|
||||||
|
// Local post
|
||||||
|
let post = get_post_by_id(db_client, &post_id).await?;
|
||||||
|
Some(post.id)
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
let post = get_post_by_object_id(db_client, &object_id).await?;
|
||||||
|
Some(post.id)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
let post_data = PostCreateData {
|
let post_data = PostCreateData {
|
||||||
content,
|
content,
|
||||||
// TODO: parse inReplyTo field
|
in_reply_to_id,
|
||||||
in_reply_to_id: None,
|
|
||||||
attachments: attachments,
|
attachments: attachments,
|
||||||
object_id: Some(object.id),
|
object_id: Some(object.id),
|
||||||
created_at: object.published,
|
created_at: object.published,
|
||||||
|
|
|
@ -213,6 +213,29 @@ pub async fn get_thread(
|
||||||
Ok(posts)
|
Ok(posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_post_by_object_id(
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
object_id: &str,
|
||||||
|
) -> Result<Post, DatabaseError> {
|
||||||
|
let maybe_row = db_client.query_opt(
|
||||||
|
"
|
||||||
|
SELECT
|
||||||
|
post, actor_profile,
|
||||||
|
ARRAY(
|
||||||
|
SELECT media_attachment
|
||||||
|
FROM media_attachment WHERE post_id = post.id
|
||||||
|
) AS attachments
|
||||||
|
FROM post
|
||||||
|
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||||
|
WHERE post.object_id = $1
|
||||||
|
",
|
||||||
|
&[&object_id],
|
||||||
|
).await?;
|
||||||
|
let row = maybe_row.ok_or(DatabaseError::NotFound("post"))?;
|
||||||
|
let post = Post::try_from(&row)?;
|
||||||
|
Ok(post)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_post_by_ipfs_cid(
|
pub async fn get_post_by_ipfs_cid(
|
||||||
db_client: &impl GenericClient,
|
db_client: &impl GenericClient,
|
||||||
ipfs_cid: &str,
|
ipfs_cid: &str,
|
||||||
|
|
Loading…
Reference in a new issue