feat: add IntoActivityPub trait, received context

finally made it into a proper trait so we can get uniform usage. also
added Context as parameter so we get app ctx while creating AP
representations
This commit is contained in:
əlemi 2024-12-26 15:38:37 +01:00
parent bb6957e638
commit 3bebd8bccf
Signed by: alemi
GPG key ID: A4895B84D311642C
22 changed files with 88 additions and 77 deletions

View file

@ -52,13 +52,13 @@ pub async fn nuke(ctx: upub::Context, for_real: bool, delete_posts: bool) -> Res
}; };
let (target, undone) = if matches!(activity.activity_type, apb::ActivityType::Follow) { let (target, undone) = if matches!(activity.activity_type, apb::ActivityType::Follow) {
(oid.clone(), activity.clone().ap()) (oid.clone(), ctx.ap(activity.clone()))
} else { } else {
let follow_activity = upub::model::activity::Entity::find_by_ap_id(oid) let follow_activity = upub::model::activity::Entity::find_by_ap_id(oid)
.one(ctx.db()) .one(ctx.db())
.await? .await?
.ok_or(sea_orm::DbErr::RecordNotFound(oid.clone()))?; .ok_or(sea_orm::DbErr::RecordNotFound(oid.clone()))?;
(follow_activity.clone().object.unwrap_or_default(), follow_activity.ap()) (follow_activity.clone().object.unwrap_or_default(), ctx.ap(follow_activity))
}; };
let aid = ctx.aid(&upub::Context::new_id()); let aid = ctx.aid(&upub::Context::new_id());

View file

@ -143,7 +143,7 @@ pub async fn relay(ctx: upub::Context, action: RelayCommand) -> Result<(), Reque
.set_id(Some(aid.clone())) .set_id(Some(aid.clone()))
.set_activity_type(Some(apb::ActivityType::Undo)) .set_activity_type(Some(apb::ActivityType::Undo))
.set_actor(apb::Node::link(ctx.base().to_string())) .set_actor(apb::Node::link(ctx.base().to_string()))
.set_object(apb::Node::object(activity.ap())) .set_object(apb::Node::object(ctx.ap(activity)))
.set_to(apb::Node::links(vec![actor.clone()])) .set_to(apb::Node::links(vec![actor.clone()]))
.set_cc(apb::Node::links(vec![apb::target::PUBLIC.to_string()])) .set_cc(apb::Node::links(vec![apb::target::PUBLIC.to_string()]))
.set_published(Some(chrono::Utc::now())); .set_published(Some(chrono::Utc::now()));
@ -179,7 +179,7 @@ pub async fn relay(ctx: upub::Context, action: RelayCommand) -> Result<(), Reque
.set_id(Some(aid.clone())) .set_id(Some(aid.clone()))
.set_activity_type(Some(apb::ActivityType::Undo)) .set_activity_type(Some(apb::ActivityType::Undo))
.set_actor(apb::Node::link(ctx.base().to_string())) .set_actor(apb::Node::link(ctx.base().to_string()))
.set_object(apb::Node::object(activity.ap())) .set_object(apb::Node::object(ctx.ap(activity)))
.set_to(apb::Node::links(vec![actor.clone()])) .set_to(apb::Node::links(vec![actor.clone()]))
.set_cc(apb::Node::links(vec![apb::target::PUBLIC.to_string()])) .set_cc(apb::Node::links(vec![apb::target::PUBLIC.to_string()]))
.set_published(Some(chrono::Utc::now())); .set_published(Some(chrono::Utc::now()));

View file

@ -114,6 +114,10 @@ impl Context {
&self.0.base_url &self.0.base_url
} }
pub fn ap<T: crate::ext::IntoActivityPub>(&self, x: T) -> serde_json::Value {
x.into_activity_pub_json(self)
}
pub fn new_id() -> String { pub fn new_id() -> String {
uuid::Uuid::new_v4().to_string() uuid::Uuid::new_v4().to_string()
} }

View file

@ -1,5 +1,9 @@
use sea_orm::{ConnectionTrait, PaginatorTrait}; use sea_orm::{ConnectionTrait, PaginatorTrait};
pub trait IntoActivityPub {
fn into_activity_pub_json(self, ctx: &crate::Context) -> serde_json::Value;
}
#[allow(async_fn_in_trait)] #[allow(async_fn_in_trait)]
pub trait AnyQuery { pub trait AnyQuery {
async fn any(self, db: &impl ConnectionTrait) -> Result<bool, sea_orm::DbErr>; async fn any(self, db: &impl ConnectionTrait) -> Result<bool, sea_orm::DbErr>;

View file

@ -87,8 +87,8 @@ impl Entity {
} }
} }
impl Model { impl crate::ext::IntoActivityPub for Model {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, _ctx: &crate::Context) -> serde_json::Value {
apb::new() apb::new()
.set_id(Some(self.id)) .set_id(Some(self.id))
.set_activity_type(Some(self.activity_type)) .set_activity_type(Some(self.activity_type))

View file

@ -200,8 +200,8 @@ impl Entity {
} }
} }
impl Model { impl crate::ext::IntoActivityPub for Model {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, _ctx: &crate::Context) -> serde_json::Value {
apb::new() apb::new()
.set_id(Some(self.id.clone())) .set_id(Some(self.id.clone()))
.set_actor_type(Some(self.actor_type)) .set_actor_type(Some(self.actor_type))

View file

@ -34,8 +34,8 @@ impl Related<super::object::Entity> for Entity {
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
impl Model { impl crate::ext::IntoActivityPub for Model {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, _ctx: &crate::Context) -> serde_json::Value {
apb::new() apb::new()
.set_url(apb::Node::link(self.url)) .set_url(apb::Node::link(self.url))
.set_document_type(Some(self.document_type)) .set_document_type(Some(self.document_type))

View file

@ -160,8 +160,8 @@ impl Entity {
} }
} }
impl Model { impl crate::ext::IntoActivityPub for Model {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, _ctx: &crate::Context) -> serde_json::Value {
apb::new() apb::new()
.set_id(Some(self.id)) .set_id(Some(self.id))
.set_object_type(Some(self.object_type)) .set_object_type(Some(self.object_type))

View file

@ -1,14 +1,16 @@
use apb::ActivityMut; use apb::ActivityMut;
use sea_orm::{DbErr, EntityName, FromQueryResult, Iden, QueryResult}; use sea_orm::{DbErr, EntityName, FromQueryResult, Iden, QueryResult};
use crate::ext::IntoActivityPub;
pub struct RichMention { pub struct RichMention {
pub mention: crate::model::mention::Model, pub mention: crate::model::mention::Model,
pub id: String, pub id: String,
pub fqn: String, pub fqn: String,
} }
impl RichMention { impl IntoActivityPub for RichMention {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, _ctx: &crate::Context) -> serde_json::Value {
use apb::LinkMut; use apb::LinkMut;
apb::new() apb::new()
.set_link_type(Some(apb::LinkType::Mention)) .set_link_type(Some(apb::LinkType::Mention))
@ -21,8 +23,8 @@ pub struct RichHashtag {
pub hash: crate::model::hashtag::Model, pub hash: crate::model::hashtag::Model,
} }
impl RichHashtag { impl IntoActivityPub for RichHashtag {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, _ctx: &crate::Context) -> serde_json::Value {
use apb::LinkMut; use apb::LinkMut;
apb::new() apb::new()
.set_name(Some(format!("#{}", self.hash.name))) .set_name(Some(format!("#{}", self.hash.name)))
@ -55,32 +57,32 @@ impl FromQueryResult for RichActivity {
} }
} }
impl RichActivity { impl IntoActivityPub for RichActivity {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, ctx: &crate::Context) -> serde_json::Value {
use apb::ObjectMut; use apb::ObjectMut;
match (self.activity, self.object) { match (self.activity, self.object) {
(None, None) => serde_json::Value::Null, (None, None) => serde_json::Value::Null,
(Some(activity), None) => { (Some(activity), None) => {
let obj = apb::Node::maybe_link(activity.object.clone()); let obj = apb::Node::maybe_link(activity.object.clone());
activity.ap().set_object(obj) activity.into_activity_pub_json(ctx).set_object(obj)
}, },
(maybe_activity, Some(object)) => { (maybe_activity, Some(object)) => {
let mut tags = Vec::new(); let mut tags = Vec::new();
if let Some(mentions) = self.mentions { if let Some(mentions) = self.mentions {
for mention in mentions { for mention in mentions {
tags.push(mention.ap()); tags.push(mention.into_activity_pub_json(ctx));
} }
} }
if let Some(hashtags) = self.hashtags { if let Some(hashtags) = self.hashtags {
for hash in hashtags { for hash in hashtags {
tags.push(hash.ap()); tags.push(hash.into_activity_pub_json(ctx));
} }
} }
let activity = match maybe_activity { let activity = match maybe_activity {
Some(activity) => activity.ap(), Some(activity) => activity.into_activity_pub_json(ctx),
None => apb::new() None => apb::new()
.set_activity_type(Some(apb::ActivityType::View)) .set_activity_type(Some(apb::ActivityType::View))
.set_published(Some(self.discovered)) .set_published(Some(self.discovered))
@ -88,13 +90,13 @@ impl RichActivity {
activity activity
.set_object(apb::Node::object( .set_object(apb::Node::object(
object.ap() object.into_activity_pub_json(ctx)
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None }) .set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
.set_tag(apb::Node::maybe_array(tags)) .set_tag(apb::Node::maybe_array(tags))
.set_attachment(match self.attachments { .set_attachment(match self.attachments {
None => apb::Node::Empty, None => apb::Node::Empty,
Some(vec) => apb::Node::array( Some(vec) => apb::Node::array(
vec.into_iter().map(|x| x.ap()).collect() vec.into_iter().map(|x| x.into_activity_pub_json(ctx)).collect()
), ),
}) })
)) ))
@ -102,35 +104,35 @@ impl RichActivity {
} }
} }
// TODO ughhh cant make it a trait because there's this different one!!! // // TODO ughhh cant make it a trait because there's this different one!!!
pub fn object_ap(self) -> serde_json::Value { // pub fn object_ap(self) -> serde_json::Value {
use apb::ObjectMut; // use apb::ObjectMut;
match self.object { // match self.object {
Some(object) => { // Some(object) => {
let mut tags = Vec::new(); // let mut tags = Vec::new();
if let Some(mentions) = self.mentions { // if let Some(mentions) = self.mentions {
for mention in mentions { // for mention in mentions {
tags.push(mention.ap()); // tags.push(mention.ap());
} // }
} // }
if let Some(hashtags) = self.hashtags { // if let Some(hashtags) = self.hashtags {
for hash in hashtags { // for hash in hashtags {
tags.push(hash.ap()); // tags.push(hash.ap());
} // }
} // }
object.ap() // object.ap()
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None }) // .set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
.set_tag(apb::Node::maybe_array(tags)) // .set_tag(apb::Node::maybe_array(tags))
.set_attachment(match self.attachments { // .set_attachment(match self.attachments {
None => apb::Node::Empty, // None => apb::Node::Empty,
Some(vec) => apb::Node::array( // Some(vec) => apb::Node::array(
vec.into_iter().map(|x| x.ap()).collect() // vec.into_iter().map(|x| x.ap()).collect()
), // ),
}) // })
}, // },
None => serde_json::Value::Null, // None => serde_json::Value::Null,
} // }
} // }
} }
pub struct RichNotification { pub struct RichNotification {
@ -150,10 +152,10 @@ impl FromQueryResult for RichNotification {
} }
} }
impl RichNotification { impl IntoActivityPub for RichNotification {
pub fn ap(self) -> serde_json::Value { fn into_activity_pub_json(self, ctx: &crate::Context) -> serde_json::Value {
let seen = self.seen; let seen = self.seen;
self.activity.ap() self.activity.into_activity_pub_json(ctx)
.set_seen(Some(seen)) .set_seen(Some(seen))
} }
} }

View file

@ -37,6 +37,6 @@ pub async fn view(
.with_batched::<upub::model::hashtag::Entity>(ctx.db()) .with_batched::<upub::model::hashtag::Entity>(ctx.db())
.await?; .await?;
Ok(JsonLD(row.ap().ld_context())) Ok(JsonLD(ctx.ap(row).ld_context()))
} }

View file

@ -41,7 +41,7 @@ pub async fn page(
.add(upub::model::addressing::Column::Actor.eq(*internal)) .add(upub::model::addressing::Column::Actor.eq(*internal))
.add(upub::model::activity::Column::Actor.eq(uid)) .add(upub::model::activity::Column::Actor.eq(uid))
.add(upub::model::object::Column::AttributedTo.eq(uid)), .add(upub::model::object::Column::AttributedTo.eq(uid)),
ctx.db(), &ctx,
page, page,
auth.my_id(), auth.my_id(),
false, false,

View file

@ -55,7 +55,7 @@ pub async fn view(
{ {
// local user // local user
Some((user_model, Some(cfg))) => { Some((user_model, Some(cfg))) => {
let mut user = user_model.ap() let mut user = ctx.ap(user_model)
.set_inbox(Node::link(upub::url!(ctx, "/actors/{id}/inbox"))) .set_inbox(Node::link(upub::url!(ctx, "/actors/{id}/inbox")))
.set_outbox(Node::link(upub::url!(ctx, "/actors/{id}/outbox"))) .set_outbox(Node::link(upub::url!(ctx, "/actors/{id}/outbox")))
.set_following(Node::link(upub::url!(ctx, "/actors/{id}/following"))) .set_following(Node::link(upub::url!(ctx, "/actors/{id}/following")))
@ -89,7 +89,7 @@ pub async fn view(
}, },
// remote user // remote user
Some((user_model, None)) => Ok(JsonLD( Some((user_model, None)) => Ok(JsonLD(
user_model.ap() ctx.ap(user_model)
.set_following_me(following_me) .set_following_me(following_me)
.set_followed_by_me(followed_by_me) .set_followed_by_me(followed_by_me)
.ld_context() .ld_context()

View file

@ -49,7 +49,7 @@ pub async fn page(
.all(ctx.db()) .all(ctx.db())
.await? .await?
.into_iter() .into_iter()
.map(|x| x.ap()) .map(|x| ctx.ap(x))
.collect(); .collect();
crate::builders::collection_page(&upub::url!(ctx, "/actors/{id}/notifications/page"), offset, limit, activities) crate::builders::collection_page(&upub::url!(ctx, "/actors/{id}/notifications/page"), offset, limit, activities)

View file

@ -31,7 +31,7 @@ pub async fn page(
crate::builders::paginate_feed( crate::builders::paginate_feed(
upub::url!(ctx, "/actors/{id}/outbox/page"), upub::url!(ctx, "/actors/{id}/outbox/page"),
filter, filter,
ctx.db(), &ctx,
page, page,
auth.my_id(), auth.my_id(),
false, false,

View file

@ -66,7 +66,7 @@ pub async fn search(
crate::builders::paginate_feed( crate::builders::paginate_feed(
upub::url!(ctx, "/search"), upub::url!(ctx, "/search"),
filter, filter,
ctx.db(), &ctx,
page, page,
auth.my_id(), auth.my_id(),
false, false,

View file

@ -22,7 +22,7 @@ pub async fn page(
crate::builders::paginate_feed( crate::builders::paginate_feed(
upub::url!(ctx, "/inbox/page"), upub::url!(ctx, "/inbox/page"),
upub::model::addressing::Column::Actor.is_null().into_condition(), upub::model::addressing::Column::Actor.is_null().into_condition(),
ctx.db(), &ctx,
page, page,
auth.my_id(), auth.my_id(),
false, false,

View file

@ -49,7 +49,7 @@ pub async fn page(
let items : Vec<serde_json::Value> = items let items : Vec<serde_json::Value> = items
.into_iter() .into_iter()
.map(|item| item.ap()) .map(|item| ctx.ap(item))
.collect(); .collect();
crate::builders::collection_page(&upub::url!(ctx, "/objects/{id}/context/page"), offset, limit, items) crate::builders::collection_page(&upub::url!(ctx, "/objects/{id}/context/page"), offset, limit, items)

View file

@ -18,7 +18,7 @@ pub async fn page(
Condition::all() Condition::all()
.add(upub::model::addressing::Column::Actor.is_null()) .add(upub::model::addressing::Column::Actor.is_null())
.add(upub::model::actor::Column::Domain.eq(ctx.domain().to_string())), .add(upub::model::actor::Column::Domain.eq(ctx.domain().to_string())),
ctx.db(), &ctx,
page, page,
auth.my_id(), auth.my_id(),
true, true,

View file

@ -39,7 +39,7 @@ pub async fn page(
.with_batched::<upub::model::hashtag::Entity>(ctx.db()) .with_batched::<upub::model::hashtag::Entity>(ctx.db())
.await? .await?
.into_iter() .into_iter()
.map(|x| x.ap()) .map(|x| ctx.ap(x))
.collect(); .collect();
crate::builders::collection_page( crate::builders::collection_page(

View file

@ -1,5 +1,5 @@
use apb::{BaseMut, CollectionMut, CollectionPageMut, LD}; use apb::{BaseMut, CollectionMut, CollectionPageMut, LD};
use sea_orm::{Condition, ConnectionTrait, QueryFilter, QuerySelect, RelationTrait, ColumnTrait}; use sea_orm::{Condition, QueryFilter, QuerySelect, RelationTrait, ColumnTrait};
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use upub::selector::{BatchFillable, RichActivity}; use upub::selector::{BatchFillable, RichActivity};
@ -9,7 +9,7 @@ use crate::activitypub::Pagination;
pub async fn paginate_feed( pub async fn paginate_feed(
id: String, id: String,
filter: Condition, filter: Condition,
db: &impl ConnectionTrait, ctx: &upub::Context,
page: Pagination, page: Pagination,
my_id: Option<i64>, my_id: Option<i64>,
with_users: bool, // TODO ewww too many arguments for this weird function... with_users: bool, // TODO ewww too many arguments for this weird function...
@ -32,6 +32,7 @@ pub async fn paginate_feed(
.join(sea_orm::JoinType::InnerJoin, upub::model::activity::Relation::Actors.def()); .join(sea_orm::JoinType::InnerJoin, upub::model::activity::Relation::Actors.def());
} }
let db = ctx.db();
let items = select let items = select
.filter(conditions) .filter(conditions)
// TODO also limit to only local activities // TODO also limit to only local activities
@ -49,7 +50,7 @@ pub async fn paginate_feed(
let items : Vec<serde_json::Value> = items let items : Vec<serde_json::Value> = items
.into_iter() .into_iter()
.map(|item| item.ap()) .map(|item| ctx.ap(item))
.collect(); .collect();
collection_page(&id, offset, limit, items) collection_page(&id, offset, limit, items)

View file

@ -21,17 +21,17 @@ pub async fn process(ctx: Context, job: &model::job::Model) -> crate::JobResult<
model::object::Entity::find_by_ap_id(oid) model::object::Entity::find_by_ap_id(oid)
.one(ctx.db()) .one(ctx.db())
.await? .await?
.map(|x| x.ap()), .map(|x| ctx.ap(x)),
apb::ActivityType::Accept(_) | apb::ActivityType::Reject(_) | apb::ActivityType::Undo => apb::ActivityType::Accept(_) | apb::ActivityType::Reject(_) | apb::ActivityType::Undo =>
model::activity::Entity::find_by_ap_id(oid) model::activity::Entity::find_by_ap_id(oid)
.one(ctx.db()) .one(ctx.db())
.await? .await?
.map(|x| x.ap()), .map(|x| ctx.ap(x)),
apb::ActivityType::Update => { apb::ActivityType::Update => {
if let Some(o) = model::object::Entity::find_by_ap_id(oid).one(ctx.db()).await? { if let Some(o) = model::object::Entity::find_by_ap_id(oid).one(ctx.db()).await? {
Some(o.ap()) Some(ctx.ap(o))
} else if let Some(a) = model::actor::Entity::find_by_ap_id(oid).one(ctx.db()).await? { } else if let Some(a) = model::actor::Entity::find_by_ap_id(oid).one(ctx.db()).await? {
Some(a.ap()) Some(ctx.ap(a))
} else { } else {
None None
} }
@ -40,7 +40,7 @@ pub async fn process(ctx: Context, job: &model::job::Model) -> crate::JobResult<
} }
} else { None }; } else { None };
let mut payload = activity.ap(); let mut payload = ctx.ap(activity);
if let Some(object) = object { if let Some(object) = object {
payload = payload.set_object(apb::Node::object(object)); payload = payload.set_object(apb::Node::object(object));
} }

View file

@ -101,7 +101,7 @@ pub async fn process(ctx: Context, job: &model::job::Model) -> crate::JobResult<
.into(); .into();
} }
updated = prev.ap(); updated = ctx.ap(prev);
}, },
apb::ObjectType::Note => { apb::ObjectType::Note => {
let mut prev = model::object::Entity::find_by_ap_id(&updated.id()?) let mut prev = model::object::Entity::find_by_ap_id(&updated.id()?)
@ -122,7 +122,7 @@ pub async fn process(ctx: Context, job: &model::job::Model) -> crate::JobResult<
prev.sensitive = sensitive; prev.sensitive = sensitive;
} }
updated = prev.ap(); updated = ctx.ap(prev);
}, },
t => return Err(crate::JobError::ProcessorError(ProcessorError::Unprocessable(format!("{t}")))), t => return Err(crate::JobError::ProcessorError(ProcessorError::Unprocessable(format!("{t}")))),
} }