feat: separate RichObject and RichActivity

This commit is contained in:
əlemi 2024-12-26 17:15:10 +01:00
parent 0c6be216b7
commit 3baa8ed839
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 107 additions and 95 deletions

View file

@ -1,7 +1,7 @@
use std::collections::{hash_map::Entry, HashMap};
use sea_orm::{ConnectionTrait, DbErr, EntityTrait, FromQueryResult, ModelTrait, QueryFilter};
use super::RichActivity;
use super::{RichActivity, RichObject};
#[allow(async_fn_in_trait)]
pub trait BatchFillable: Sized {
@ -9,17 +9,49 @@ pub trait BatchFillable: Sized {
where
E: BatchFillableComparison + EntityTrait,
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
RichActivity: BatchFillableAcceptor<Vec<E::Model>>;
RichObject: BatchFillableAcceptor<Vec<E::Model>>;
}
impl BatchFillable for RichActivity {
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
where
E: BatchFillableComparison + EntityTrait,
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
RichObject: BatchFillableAcceptor<Vec<E::Model>>,
{
if let Some(obj) = self.object {
self.object = Some(obj.with_batched::<E>(tx).await?);
}
Ok(self)
}
}
impl BatchFillable for Vec<RichActivity> {
async fn with_batched<E>(self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
where
E: BatchFillableComparison + EntityTrait,
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
RichObject: BatchFillableAcceptor<Vec<E::Model>>
{
// TODO can we do this in-place rather than copying everything to a new vec?
let mut out = Vec::new();
for item in self {
out.push(item.with_batched::<E>(tx).await?);
}
Ok(out)
}
}
impl BatchFillable for Vec<RichActivity> {
impl BatchFillable for Vec<RichObject> {
// TODO 3 iterations... can we make it in less passes?
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
where
E: BatchFillableComparison + EntityTrait,
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
RichActivity: BatchFillableAcceptor<Vec<E::Model>>,
RichObject: BatchFillableAcceptor<Vec<E::Model>>,
{
let ids : Vec<i64> = self.iter().filter_map(|x| Some(x.object.as_ref()?.internal)).collect();
let batch = E::find()
@ -46,12 +78,12 @@ impl BatchFillable for Vec<RichActivity> {
}
}
impl BatchFillable for RichActivity {
impl BatchFillable for RichObject {
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
where
E: BatchFillableComparison + EntityTrait,
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
RichActivity: BatchFillableAcceptor<Vec<E::Model>>,
RichObject: BatchFillableAcceptor<Vec<E::Model>>,
{
if let Some(ref obj) = self.object {
let batch =E::find()
@ -69,7 +101,7 @@ impl BatchFillable for RichActivity {
mod hell {
use sea_orm::{sea_query::IntoCondition, ColumnTrait, ConnectionTrait, DbErr, EntityTrait};
use crate::selector::rich::{RichHashtag, RichMention};
use crate::selector::rich::{RichHashtag, RichMention};
pub trait BatchFillableComparison {
fn comparison(ids: Vec<i64>) -> sea_orm::Condition;
@ -115,26 +147,26 @@ use crate::selector::rich::{RichHashtag, RichMention};
}
}
#[allow(async_fn_in_trait)]
#[allow(async_fn_in_trait)]
pub trait BatchFillableAcceptor<B> {
async fn accept(&mut self, batch: B, tx: &impl ConnectionTrait) -> Result<(), DbErr>;
}
impl BatchFillableAcceptor<Vec<crate::model::attachment::Model>> for super::RichActivity {
impl BatchFillableAcceptor<Vec<crate::model::attachment::Model>> for super::RichObject {
async fn accept(&mut self, batch: Vec<crate::model::attachment::Model>, _tx: &impl ConnectionTrait) -> Result<(), DbErr> {
self.attachments = Some(batch);
Ok(())
}
}
impl BatchFillableAcceptor<Vec<crate::model::hashtag::Model>> for super::RichActivity {
impl BatchFillableAcceptor<Vec<crate::model::hashtag::Model>> for super::RichObject {
async fn accept(&mut self, batch: Vec<crate::model::hashtag::Model>, _tx: &impl ConnectionTrait) -> Result<(), DbErr> {
self.hashtags = Some(batch.into_iter().map(|x| RichHashtag { hash: x }).collect());
Ok(())
}
}
impl BatchFillableAcceptor<Vec<crate::model::mention::Model>> for super::RichActivity {
impl BatchFillableAcceptor<Vec<crate::model::mention::Model>> for super::RichObject {
async fn accept(&mut self, batch: Vec<crate::model::mention::Model>, tx: &impl ConnectionTrait) -> Result<(), DbErr> {
// TODO batch load users from mentions rather than doing for loop
let mut mentions = Vec::new();

View file

@ -5,8 +5,4 @@ mod query;
pub use query::Query;
mod rich;
pub use rich::{RichActivity, RichNotification};
pub use rich::{RichActivity, RichObject, RichNotification};

View file

@ -58,11 +58,6 @@ impl Query {
select = select.select_column_as(col, format!("{}{}", model::object::Entity.table_name(), col.to_string()));
}
select = select.select_column_as(
model::addressing::Column::Published,
format!("{}{}", model::addressing::Entity.table_name(), model::addressing::Column::Published.to_string())
);
if let Some(uid) = my_id {
select = select
.join(

View file

@ -32,23 +32,70 @@ impl IntoActivityPub for RichHashtag {
}
}
pub struct RichActivity {
pub activity: Option<crate::model::activity::Model>,
pub struct RichObject {
pub object: Option<crate::model::object::Model>,
pub liked: Option<i64>,
pub attachments: Option<Vec<crate::model::attachment::Model>>,
pub hashtags: Option<Vec<RichHashtag>>,
pub mentions: Option<Vec<RichMention>>,
}
impl FromQueryResult for RichObject {
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
Ok(RichObject {
attachments: None,
hashtags: None,
mentions: None,
liked: res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok(),
object: crate::model::object::Model::from_query_result_optional(res, crate::model::object::Entity.table_name())?,
})
}
}
impl IntoActivityPub for RichObject {
fn into_activity_pub_json(self, ctx: &crate::Context) -> serde_json::Value {
use apb::ObjectMut;
match self.object {
Some(object) => {
let mut tags = Vec::new();
if let Some(mentions) = self.mentions {
for mention in mentions {
tags.push(mention.into_activity_pub_json(ctx));
}
}
if let Some(hashtags) = self.hashtags {
for hash in hashtags {
tags.push(hash.into_activity_pub_json(ctx));
}
}
object.into_activity_pub_json(ctx)
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
.set_tag(apb::Node::maybe_array(tags))
.set_attachment(match self.attachments {
None => apb::Node::Empty,
Some(vec) => apb::Node::array(
vec.into_iter()
.map(|x| x.into_activity_pub_json(ctx))
.collect()
),
})
},
None => serde_json::Value::Null,
}
}
}
pub struct RichActivity {
pub activity: Option<crate::model::activity::Model>,
pub object: Option<RichObject>,
pub discovered: chrono::DateTime<chrono::Utc>,
}
impl FromQueryResult for RichActivity {
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
Ok(RichActivity {
attachments: None, hashtags: None, mentions: None,
liked: res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok(),
object: crate::model::object::Model::from_query_result(res, crate::model::object::Entity.table_name()).ok(),
activity: crate::model::activity::Model::from_query_result(res, crate::model::activity::Entity.table_name()).ok(),
object: RichObject::from_query_result_optional(res, _pre)?,
activity: crate::model::activity::Model::from_query_result_optional(res, crate::model::activity::Entity.table_name())?,
discovered: res.try_get(
crate::model::addressing::Entity.table_name(),
&crate::model::addressing::Column::Published.to_string()
@ -63,76 +110,18 @@ impl IntoActivityPub for RichActivity {
match (self.activity, self.object) {
(None, None) => serde_json::Value::Null,
(Some(activity), None) => {
let obj = apb::Node::maybe_link(activity.object.clone());
activity.into_activity_pub_json(ctx).set_object(obj)
},
(Some(activity), None) => activity.into_activity_pub_json(ctx),
(maybe_activity, Some(object)) => {
let mut tags = Vec::new();
if let Some(mentions) = self.mentions {
for mention in mentions {
tags.push(mention.into_activity_pub_json(ctx));
}
}
if let Some(hashtags) = self.hashtags {
for hash in hashtags {
tags.push(hash.into_activity_pub_json(ctx));
}
}
(None, Some(object)) => apb::new()
.set_activity_type(Some(apb::ActivityType::View))
.set_published(Some(self.discovered))
.set_object(apb::Node::object(object.into_activity_pub_json(ctx))),
let activity = match maybe_activity {
Some(activity) => activity.into_activity_pub_json(ctx),
None => apb::new()
.set_activity_type(Some(apb::ActivityType::View))
.set_published(Some(self.discovered))
};
activity
.set_object(apb::Node::object(
object.into_activity_pub_json(ctx)
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
.set_tag(apb::Node::maybe_array(tags))
.set_attachment(match self.attachments {
None => apb::Node::Empty,
Some(vec) => apb::Node::array(
vec.into_iter().map(|x| x.into_activity_pub_json(ctx)).collect()
),
})
))
},
(Some(activity), Some(object)) => activity
.into_activity_pub_json(ctx)
.set_object(apb::Node::object(object.into_activity_pub_json(ctx))),
}
}
// // TODO ughhh cant make it a trait because there's this different one!!!
// pub fn object_ap(self) -> serde_json::Value {
// use apb::ObjectMut;
// match self.object {
// Some(object) => {
// let mut tags = Vec::new();
// if let Some(mentions) = self.mentions {
// for mention in mentions {
// tags.push(mention.ap());
// }
// }
// if let Some(hashtags) = self.hashtags {
// for hash in hashtags {
// tags.push(hash.ap());
// }
// }
// object.ap()
// .set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
// .set_tag(apb::Node::maybe_array(tags))
// .set_attachment(match self.attachments {
// None => apb::Node::Empty,
// Some(vec) => apb::Node::array(
// vec.into_iter().map(|x| x.ap()).collect()
// ),
// })
// },
// None => serde_json::Value::Null,
// }
// }
}
pub struct RichNotification {

View file

@ -1,6 +1,6 @@
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, Order, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect};
use upub::{model, selector::{BatchFillable, RichActivity}, Context};
use upub::{model, selector::{BatchFillable, RichObject}, Context};
use crate::{activitypub::Pagination, builders::JsonLD, AuthIdentity};
@ -36,7 +36,7 @@ pub async fn page(
.order_by(model::object::Column::Published, Order::Asc)
.limit(limit)
.offset(offset)
.into_model::<RichActivity>()
.into_model::<RichObject>()
.all(ctx.db())
.await?
.with_batched::<upub::model::attachment::Entity>(ctx.db())