feat: separate RichObject and RichActivity
This commit is contained in:
parent
0c6be216b7
commit
3baa8ed839
5 changed files with 107 additions and 95 deletions
|
@ -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()
|
||||
|
@ -120,21 +152,21 @@ use crate::selector::rich::{RichHashtag, RichMention};
|
|||
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();
|
||||
|
|
|
@ -5,8 +5,4 @@ mod query;
|
|||
pub use query::Query;
|
||||
|
||||
mod rich;
|
||||
pub use rich::{RichActivity, RichNotification};
|
||||
|
||||
|
||||
|
||||
|
||||
pub use rich::{RichActivity, RichObject, RichNotification};
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
let activity = match maybe_activity {
|
||||
Some(activity) => activity.into_activity_pub_json(ctx),
|
||||
None => apb::new()
|
||||
(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))),
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in a new issue