Implement Blog::to_activity07(), outbox_collection07() and outbox_collection_page07()

This commit is contained in:
Kitaiti Makoto 2022-02-24 02:40:36 +09:00
parent 994a4dbb2d
commit 74d6dc5089

View File

@ -9,9 +9,13 @@ use activitypub::{
CustomObject, CustomObject,
}; };
use activitystreams::{ use activitystreams::{
actor::AsApActor, actor::{ApActor, ApActorExt, AsApActor, Group as Group07},
base::AnyBase, base::AnyBase,
object::{kind::ImageType, ApObject, ApObjectExt, Image as Image07, ObjectExt}, collection::{
OrderedCollection as OrderedCollection07, OrderedCollectionPage as OrderedCollectionPage07,
},
iri_string::types::IriString,
object::{kind::ImageType, ApObject, Image as Image07, ObjectExt},
prelude::*, prelude::*,
}; };
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
@ -24,15 +28,15 @@ use openssl::{
}; };
use plume_common::activity_pub::{ use plume_common::activity_pub::{
inbox::{AsActor, FromId, FromId07}, inbox::{AsActor, FromId, FromId07},
sign, ActivityStream, ApSignature, CustomGroup as CustomGroup07, Id, IntoId, PublicKey, Source, sign, ActivityStream, ActorSource, ApSignature, ApSignature07, CustomGroup as CustomGroup07,
ToAsString, ToAsUri, Id, IntoId, PublicKey, PublicKey07, Source, ToAsString, ToAsUri,
}; };
use url::Url; use url::Url;
use webfinger::*; use webfinger::*;
pub type CustomGroup = CustomObject<ApSignature, Group>; pub type CustomGroup = CustomObject<ApSignature, Group>;
#[derive(Queryable, Identifiable, Clone, AsChangeset)] #[derive(Queryable, Identifiable, Clone, AsChangeset, Debug)]
#[changeset_options(treat_none_as_null = "true")] #[changeset_options(treat_none_as_null = "true")]
pub struct Blog { pub struct Blog {
pub id: i32, pub id: i32,
@ -229,9 +233,73 @@ impl Blog {
Ok(CustomGroup::new(blog, ap_signature)) Ok(CustomGroup::new(blog, ap_signature))
} }
pub fn to_activity07(&self, conn: &Connection) -> Result<CustomGroup07> {
let mut blog = ApActor::new(self.inbox_url.parse()?, Group07::new());
blog.set_preferred_username(self.actor_id.clone());
blog.set_name(self.title.clone());
blog.set_outbox(self.outbox_url.parse()?);
blog.set_summary(self.summary_html.to_string());
let source = ActorSource {
source: Source {
content: self.summary.clone(),
media_type: String::from("text/markdown"),
},
};
let mut icon = Image07::new();
let _ = self.icon_id.map(|id| {
Media::get(conn, id).and_then(|m| {
let _ = m
.url()
.and_then(|url| url.parse::<IriString>().map_err(|_| Error::Url))
.map(|url| icon.set_url(url));
icon.set_attributed_to(
User::get(conn, m.owner_id)?
.into_id()
.parse::<IriString>()?,
);
Ok(())
})
});
blog.set_icon(icon.into_any_base()?);
let mut banner = Image07::new();
let _ = self.banner_id.map(|id| {
Media::get(conn, id).and_then(|m| {
let _ = m
.url()
.and_then(|url| url.parse::<IriString>().map_err(|_| Error::Url))
.map(|url| banner.set_url(url));
banner.set_attributed_to(
User::get(conn, m.owner_id)?
.into_id()
.parse::<IriString>()?,
);
Ok(())
})
});
blog.set_image(banner.into_any_base()?);
blog.set_id(self.ap_url.parse()?);
let pub_key = PublicKey07 {
id: format!("{}#main-key", self.ap_url).parse()?,
owner: self.ap_url.parse()?,
public_key_pem: self.public_key.clone(),
};
let ap_signature = ApSignature07 {
public_key: pub_key,
};
// assert_eq!("sumhtml", &self.summary_html.to_string());
// assert_eq!("sum", blog.summary().unwrap().as_single_xsd_string().unwrap());
// assert_eq!(json!({}), serde_json::to_value(&blog).unwrap());
Ok(CustomGroup07::new(blog, ap_signature, source))
}
pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<OrderedCollection>> { pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<OrderedCollection>> {
self.outbox_collection(conn) self.outbox_collection(conn).map(ActivityStream::new)
.map(|coll| ActivityStream::new(coll))
} }
pub fn outbox_collection(&self, conn: &Connection) -> Result<OrderedCollection> { pub fn outbox_collection(&self, conn: &Connection) -> Result<OrderedCollection> {
let mut coll = OrderedCollection::default(); let mut coll = OrderedCollection::default();
@ -248,13 +316,34 @@ impl Blog {
)))?; )))?;
Ok(coll) Ok(coll)
} }
pub fn outbox_collection07(&self, conn: &Connection) -> Result<OrderedCollection07> {
let acts = self.get_activities(conn);
let acts = acts
.iter()
.filter_map(|value| AnyBase::from_arbitrary_json(value).ok())
.collect::<Vec<AnyBase>>();
let n_acts = acts.len();
let mut coll = OrderedCollection07::new();
coll.set_many_items(acts);
coll.set_total_items(n_acts as u64);
coll.set_first(format!("{}?page=1", &self.outbox_url).parse::<IriString>()?);
coll.set_last(
format!(
"{}?page={}",
&self.outbox_url,
(n_acts as u64 + ITEMS_PER_PAGE as u64 - 1) as u64 / ITEMS_PER_PAGE as u64
)
.parse::<IriString>()?,
);
Ok(coll)
}
pub fn outbox_page( pub fn outbox_page(
&self, &self,
conn: &Connection, conn: &Connection,
(min, max): (i32, i32), (min, max): (i32, i32),
) -> Result<ActivityStream<OrderedCollectionPage>> { ) -> Result<ActivityStream<OrderedCollectionPage>> {
self.outbox_collection_page(conn, (min, max)) self.outbox_collection_page(conn, (min, max))
.map(|coll| ActivityStream::new(coll)) .map(ActivityStream::new)
} }
pub fn outbox_collection_page( pub fn outbox_collection_page(
&self, &self,
@ -278,6 +367,29 @@ impl Blog {
coll.collection_props.items = serde_json::to_value(acts)?; coll.collection_props.items = serde_json::to_value(acts)?;
Ok(coll) Ok(coll)
} }
pub fn outbox_collection_page07(
&self,
conn: &Connection,
(min, max): (i32, i32),
) -> Result<OrderedCollectionPage07> {
let mut coll = OrderedCollectionPage07::new();
let acts = self.get_activity_page(conn, (min, max));
//This still doesn't do anything because the outbox
//doesn't do anything yet
coll.set_next(
format!("{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE + 1)
.parse::<IriString>()?,
);
coll.set_prev(
format!("{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE - 1)
.parse::<IriString>()?,
);
coll.set_many_items(
acts.iter()
.filter_map(|value| AnyBase::from_arbitrary_json(value).ok()),
);
Ok(coll)
}
fn get_activities(&self, _conn: &Connection) -> Vec<serde_json::Value> { fn get_activities(&self, _conn: &Connection) -> Vec<serde_json::Value> {
vec![] vec![]
} }
@ -506,7 +618,7 @@ impl FromId07<DbConn> for Blog {
new_blog.title = object new_blog.title = object
.name() .name()
.and_then(|name| name.to_as_string()) .and_then(|name| name.to_as_string())
.unwrap_or_else(|| name); .unwrap_or(name);
new_blog.summary_html = SafeString::new( new_blog.summary_html = SafeString::new(
&object &object
.summary() .summary()
@ -548,12 +660,7 @@ impl FromId07<DbConn> for Blog {
.map(|m| m.id); .map(|m| m.id);
new_blog.banner_id = banner_id; new_blog.banner_id = banner_id;
let source = object new_blog.summary = acct.ext_two.source.content;
.source()
.and_then(|s| s.as_xsd_string())
.unwrap_or_default()
.to_string();
new_blog.summary = source;
let any_base = AnyBase::from_extended(object)?; let any_base = AnyBase::from_extended(object)?;
let id = any_base.id().ok_or(Error::MissingApProperty)?; let id = any_base.id().ok_or(Error::MissingApProperty)?;
@ -667,7 +774,7 @@ pub(crate) mod tests {
pub(crate) fn fill_database(conn: &Conn) -> (Vec<User>, Vec<Blog>) { pub(crate) fn fill_database(conn: &Conn) -> (Vec<User>, Vec<Blog>) {
instance_tests::fill_database(conn); instance_tests::fill_database(conn);
let users = usersTests::fill_database(conn); let users = usersTests::fill_database(conn);
let blog1 = Blog::insert( let mut blog1 = Blog::insert(
conn, conn,
NewBlog::new_local( NewBlog::new_local(
"BlogName".to_owned(), "BlogName".to_owned(),
@ -741,6 +848,40 @@ pub(crate) mod tests {
) )
.unwrap(); .unwrap();
blog1.icon_id = Some(
Media::insert(
conn,
NewMedia {
file_path: "aaa.png".into(),
alt_text: String::new(),
is_remote: false,
remote_url: None,
sensitive: false,
content_warning: None,
owner_id: users[0].id,
},
)
.unwrap()
.id,
);
blog1.banner_id = Some(
Media::insert(
conn,
NewMedia {
file_path: "bbb.png".into(),
alt_text: String::new(),
is_remote: false,
remote_url: None,
sensitive: false,
content_warning: None,
owner_id: users[0].id,
},
)
.unwrap()
.id,
);
let _: Blog = blog1.save_changes(&*conn).unwrap();
for i in 1..(ITEMS_PER_PAGE * 4 + 3) { for i in 1..(ITEMS_PER_PAGE * 4 + 3) {
let title = format!("Post {}", i); let title = format!("Post {}", i);
let content = format!("Content for post {}.", i); let content = format!("Content for post {}.", i);
@ -1031,6 +1172,33 @@ pub(crate) mod tests {
#[test] #[test]
fn self_federation() { fn self_federation() {
let conn = &db();
conn.test_transaction::<_, (), _>(|| {
let (_users, blogs) = fill_database(&conn);
let ap_repr = blogs[0].to_activity(&conn).unwrap();
blogs[0].delete(&conn).unwrap();
let blog = Blog::from_activity(&conn, ap_repr).unwrap();
assert_eq!(blog.actor_id, blogs[0].actor_id);
assert_eq!(blog.title, blogs[0].title);
assert_eq!(blog.summary, blogs[0].summary);
assert_eq!(blog.outbox_url, blogs[0].outbox_url);
assert_eq!(blog.inbox_url, blogs[0].inbox_url);
assert_eq!(blog.instance_id, blogs[0].instance_id);
assert_eq!(blog.ap_url, blogs[0].ap_url);
assert_eq!(blog.public_key, blogs[0].public_key);
assert_eq!(blog.fqn, blogs[0].fqn);
assert_eq!(blog.summary_html, blogs[0].summary_html);
assert_eq!(blog.icon_url(&conn), blogs[0].icon_url(&conn));
assert_eq!(blog.banner_url(&conn), blogs[0].banner_url(&conn));
Ok(())
})
}
#[test]
fn self_federation07() {
let conn = &db(); let conn = &db();
conn.test_transaction::<_, (), _>(|| { conn.test_transaction::<_, (), _>(|| {
let (users, mut blogs) = fill_database(&conn); let (users, mut blogs) = fill_database(&conn);
@ -1067,10 +1235,9 @@ pub(crate) mod tests {
.id, .id,
); );
let _: Blog = blogs[0].save_changes(&**conn).unwrap(); let _: Blog = blogs[0].save_changes(&**conn).unwrap();
let ap_repr = blogs[0].to_activity07(&conn).unwrap();
let ap_repr = blogs[0].to_activity(&conn).unwrap();
blogs[0].delete(&conn).unwrap(); blogs[0].delete(&conn).unwrap();
let blog = Blog::from_activity(&conn, ap_repr).unwrap(); let blog = Blog::from_activity07(&conn, ap_repr).unwrap();
assert_eq!(blog.actor_id, blogs[0].actor_id); assert_eq!(blog.actor_id, blogs[0].actor_id);
assert_eq!(blog.title, blogs[0].title); assert_eq!(blog.title, blogs[0].title);
@ -1101,15 +1268,15 @@ pub(crate) mod tests {
"followers": null, "followers": null,
"following": null, "following": null,
"icon": { "icon": {
"attributedTo": "", "attributedTo": "https://plu.me/@/admin/",
"type": "Image", "type": "Image",
"url": "" "url": "https://plu.me/aaa.png"
}, },
"id": "https://plu.me/~/BlogName/", "id": "https://plu.me/~/BlogName/",
"image": { "image": {
"attributedTo": "", "attributedTo": "https://plu.me/@/admin/",
"type": "Image", "type": "Image",
"url": "" "url": "https://plu.me/bbb.png"
}, },
"inbox": "https://plu.me/~/BlogName/inbox", "inbox": "https://plu.me/~/BlogName/inbox",
"liked": null, "liked": null,
@ -1135,6 +1302,49 @@ pub(crate) mod tests {
}); });
} }
#[test]
fn to_activity07() {
let conn = &db();
conn.test_transaction::<_, Error, _>(|| {
let (_users, blogs) = fill_database(&conn);
let blog = &blogs[0];
let act = blog.to_activity07(conn)?;
let expected = json!({
"icon": {
"attributedTo": "https://plu.me/@/admin/",
"type": "Image",
"url": "https://plu.me/aaa.png"
},
"id": "https://plu.me/~/BlogName/",
"image": {
"attributedTo": "https://plu.me/@/admin/",
"type": "Image",
"url": "https://plu.me/bbb.png"
},
"inbox": "https://plu.me/~/BlogName/inbox",
"name": "Blog name",
"outbox": "https://plu.me/~/BlogName/outbox",
"preferredUsername": "BlogName",
"publicKey": {
"id": "https://plu.me/~/BlogName/#main-key",
"owner": "https://plu.me/~/BlogName/",
"publicKeyPem": blog.public_key
},
"source": {
"content": "This is a small blog",
"mediaType": "text/markdown"
},
"summary": "",
"type": "Group"
});
assert_json_eq!(to_value(act)?, expected);
Ok(())
});
}
#[test] #[test]
fn outbox_collection() { fn outbox_collection() {
let conn = &db(); let conn = &db();
@ -1157,6 +1367,28 @@ pub(crate) mod tests {
}); });
} }
#[test]
fn outbox_collection07() {
let conn = &db();
conn.test_transaction::<_, Error, _>(|| {
let (_users, blogs) = fill_database(conn);
let blog = &blogs[0];
let act = blog.outbox_collection07(conn)?;
let expected = json!({
"items": [],
"totalItems": 0,
"first": "https://plu.me/~/BlogName/outbox?page=1",
"last": "https://plu.me/~/BlogName/outbox?page=0",
"type": "OrderedCollection"
});
assert_json_eq!(to_value(act)?, expected);
Ok(())
});
}
#[test] #[test]
fn outbox_collection_page() { fn outbox_collection_page() {
let conn = &db(); let conn = &db();
@ -1177,4 +1409,25 @@ pub(crate) mod tests {
Ok(()) Ok(())
}); });
} }
#[test]
fn outbox_collection_page07() {
let conn = &db();
conn.test_transaction::<_, Error, _>(|| {
let (_users, blogs) = fill_database(conn);
let blog = &blogs[0];
let act = blog.outbox_collection_page07(conn, (33, 36))?;
let expected = json!({
"next": "https://plu.me/~/BlogName/outbox?page=3",
"prev": "https://plu.me/~/BlogName/outbox?page=1",
"items": [],
"type": "OrderedCollectionPage"
});
assert_json_eq!(to_value(act)?, expected);
Ok(())
});
}
} }