Allow attributedTo to be array of values

Initial support for PeerTube.
This commit is contained in:
silverpill 2021-12-24 15:13:25 +00:00
parent 91a91b9c16
commit dc281f821f
2 changed files with 39 additions and 9 deletions

View file

@ -64,7 +64,7 @@ pub struct Object {
pub published: Option<DateTime<Utc>>, pub published: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub attributed_to: Option<String>, pub attributed_to: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub in_reply_to: Option<String>, pub in_reply_to: Option<String>,

View file

@ -8,7 +8,7 @@ use uuid::Uuid;
use crate::config::{Config, Instance}; use crate::config::{Config, Instance};
use crate::database::{Pool, get_database_client}; use crate::database::{Pool, get_database_client};
use crate::errors::{DatabaseError, HttpError, ValidationError}; use crate::errors::{ConversionError, DatabaseError, HttpError, ValidationError};
use crate::models::attachments::queries::create_attachment; use crate::models::attachments::queries::create_attachment;
use crate::models::posts::mentions::mention_to_address; use crate::models::posts::mentions::mention_to_address;
use crate::models::posts::queries::{ use crate::models::posts::queries::{
@ -88,15 +88,30 @@ fn parse_object_id(
Ok(internal_object_id) Ok(internal_object_id)
} }
fn parse_array(value: &Value) -> Result<Vec<String>, ValidationError> { fn parse_array(value: &Value) -> Result<Vec<String>, ConversionError> {
let result = match value { let result = match value {
Value::String(string) => vec![string.to_string()], Value::String(string) => vec![string.to_string()],
Value::Array(array) => { Value::Array(array) => {
array.iter() let mut results = vec![];
.filter_map(|val| val.as_str().map(|s| s.to_string())) for value in array {
.collect() match value {
Value::String(string) => results.push(string.to_string()),
Value::Object(object) => {
if let Some(string) = object["id"].as_str() {
results.push(string.to_string());
} else {
// id property is missing
return Err(ConversionError);
};
},
// Unexpected array item type
_ => return Err(ConversionError),
};
};
results
}, },
_ => return Err(ValidationError("invalid attribute value")), // Unexpected value type
_ => return Err(ConversionError),
}; };
Ok(result) Ok(result)
} }
@ -242,10 +257,15 @@ pub async fn process_note(
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"))?;
let author_id = parse_array(&attributed_to)
.map_err(|_| ValidationError("invalid attributedTo property"))?
.get(0)
.ok_or(ValidationError("invalid attributedTo property"))?
.to_string();
let author = get_or_fetch_profile_by_actor_id( let author = get_or_fetch_profile_by_actor_id(
db_client, db_client,
&instance, &instance,
&attributed_to, &author_id,
&config.media_dir(), &config.media_dir(),
).await?; ).await?;
let content = object.content let content = object.content
@ -327,7 +347,8 @@ pub async fn process_note(
}; };
let visibility = match object.to { let visibility = match object.to {
Some(value) => { Some(value) => {
let recipients = parse_array(&value)?; let recipients = parse_array(&value)
.map_err(|_| ValidationError("invalid 'to' property value"))?;
if recipients.len() == 1 && if recipients.len() == 1 &&
parse_actor_id(&instance.url(), &recipients[0]).is_ok() parse_actor_id(&instance.url(), &recipients[0]).is_ok()
{ {
@ -627,6 +648,15 @@ mod tests {
); );
} }
#[test]
fn test_parse_array_with_array_of_objects() {
let value = json!([{"id": "test1"}, {"id": "test2"}]);
assert_eq!(
parse_array(&value).unwrap(),
vec!["test1".to_string(), "test2".to_string()],
);
}
#[test] #[test]
fn test_get_object_id_from_string() { fn test_get_object_id_from_string() {
let value = json!("test_id"); let value = json!("test_id");