Add template field to Link because ostatus subscribe schema needs it.
All checks were successful
/ cargo test (push) Successful in 21s

Also fixes a bug where Resource::compress lost every link and adds a lot more tests around that functionality.
This commit is contained in:
Károly Veres 2024-05-07 17:58:30 +02:00
parent 8eafcf86b9
commit 4ef831c81c

View file

@ -114,6 +114,7 @@ impl Resource {
.map(clone_hashmap_with_option_value_as_complete)
.unwrap_or_default(),
),
template: Some(link.template.clone().unwrap_or_default()),
}
}))
})
@ -146,6 +147,36 @@ impl Resource {
.links
.filter(|links| !links.is_empty())
.map(|mut links| {
// Collapse default subvalues
for link in &mut links {
if link.media_type.as_ref().map_or(false, String::is_empty) {
link.media_type = None;
}
if link.href.as_ref().map_or(false, String::is_empty) {
link.href = None;
}
if let Some(titles) = &mut link.titles {
titles.retain(|key, value| !key.is_empty() || !value.is_empty())
}
if link.titles.as_ref().map_or(false, HashMap::is_empty) {
link.titles = None;
}
if let Some(properties) = &mut link.properties {
for value in properties.values_mut() {
if value.as_ref().map_or(false, String::is_empty) {
*value = None;
}
}
properties.retain(|key, value| !key.is_empty() || !value.is_none())
}
if link.properties.as_ref().map_or(false, HashMap::is_empty) {
link.properties = None;
}
if link.template.as_ref().map_or(false, String::is_empty) {
link.template = None;
}
}
// Delete completely default links
links.retain(|link| {
// Empty `rel` is invalid, but short-circuiting here would delete records
// that are only partially edited. Better to store invalid data than to delete
@ -153,22 +184,19 @@ impl Resource {
let mut is_default = link.rel.is_empty();
is_default &= link
.media_type
.as_ref()
.filter(|media_type| !media_type.is_empty())
.is_none();
is_default &= link.href.as_ref().filter(|href| !href.is_empty()).is_none();
is_default &= link
.titles
.as_ref()
.filter(|titles| !titles.is_empty())
.is_none();
is_default &= link
.properties
.as_ref()
.filter(|titles| !titles.is_empty())
.is_none();
is_default &= link
.template
.is_none();
is_default
!is_default
});
links
@ -189,12 +217,15 @@ pub struct Link {
pub titles: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<HashMap<String, Option<String>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub template: Option<String>,
}
#[cfg(test)]
/// Functions to generate data for testing functions that manipulate `Resource` structs
pub mod test_data {
use crate::schema::resource::Resource;
use std::collections::HashMap;
use crate::schema::resource::{Link, Resource};
/// A [`Resource`] with only the `subject` field set
pub fn barebones_user() -> Resource {
@ -220,6 +251,100 @@ pub mod test_data {
links: None,
}
}
pub fn example_mastodon_user() -> Resource {
Resource {
subject: "acct:user@example.com".to_string(),
aliases: Some(vec![
"https://example.com/@user".to_string(),
"https://example.com/users/user".to_string()
]),
properties: None,
links: Some(vec![
Link {
rel: "http://webfinger.net/rel/profile-page".to_string(),
media_type: Some("text/html".to_string()),
href: Some("https://example.com/@user".to_string()),
titles: None,
properties: None,
template: None,
},
Link {
rel: "self".to_string(),
media_type: Some("application/activity+json".to_string()),
href: Some("https://example.com/users/user".to_string()),
titles: None,
properties: None,
template: None,
},
Link {
rel: "http://ostatus.org/schema/1.0/subscribe".to_string(),
media_type: None,
href: None,
titles: None,
properties: None,
template: Some("https://example.com/authorize_interaction?uri={uri}".to_string()),
},
Link {
rel: "http://webfinger.net/rel/avatar".to_string(),
media_type: Some("image/png".to_string()),
href: Some("https://example.com/system/accounts/avatars/321/423/112/234/123/527/original/1j2ioff88a9wa.png".to_string()),
titles: None,
properties: None,
template: None,
}
]),
}
}
pub fn example_firefish_user() -> Resource {
Resource {
subject: "acct:user@example.com".to_string(),
aliases: None,
properties: None,
links: Some(vec![
Link {
rel: "self".to_string(),
media_type: Some("application/activity+json".to_string()),
href: Some("https://example.com/users/8fsua89lcieaj".to_string()),
titles: None,
properties: None,
template: None,
},
Link {
rel: "http://webfinger.net/rel/profile-page".to_string(),
media_type: Some("text/html".to_string()),
href: Some("https://example.com/@user".to_string()),
titles: None,
properties: None,
template: None,
},
Link {
rel: "http://ostatus.org/schema/1.0/subscribe".to_string(),
media_type: None,
href: None,
titles: None,
properties: None,
template: Some("https://example.com/authorize-follow?acct={uri}".to_string()),
}
])
}
}
pub fn full_resource() -> Resource {
Resource {
subject: "some_subject".to_string(),
aliases: Some(vec!["ThisIsAnAlias".to_string()]),
properties: Some(HashMap::from_iter(std::iter::once(("property_key".to_string(), Some("property_value".to_string()))))),
links: Some(vec![Link {
rel: "link_relation".to_string(),
media_type: Some("media/type".to_string()),
href: Some("https://example.com/link_href".to_string()),
titles: Some(HashMap::from_iter(std::iter::once(("title_key".to_string(), "title_value".to_string())))),
properties: Some(HashMap::from_iter(std::iter::once(("property_key".to_string(), Some("property_value".to_string()))))),
template: Some("template".to_string()),
}]),
}
}
}
#[cfg(test)]
@ -288,6 +413,9 @@ mod tests {
test_data::barebones_user(),
test_data::user_with_matching_subject_and_alias(),
test_data::user_with_single_alias(),
test_data::example_firefish_user(),
test_data::example_mastodon_user(),
test_data::full_resource(),
] {
assert_eq!(data, data.as_completely_serializable().compress());
}