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 std::collections::{hash_map::Entry, HashMap};
|
||||||
|
|
||||||
use sea_orm::{ConnectionTrait, DbErr, EntityTrait, FromQueryResult, ModelTrait, QueryFilter};
|
use sea_orm::{ConnectionTrait, DbErr, EntityTrait, FromQueryResult, ModelTrait, QueryFilter};
|
||||||
use super::RichActivity;
|
use super::{RichActivity, RichObject};
|
||||||
|
|
||||||
#[allow(async_fn_in_trait)]
|
#[allow(async_fn_in_trait)]
|
||||||
pub trait BatchFillable: Sized {
|
pub trait BatchFillable: Sized {
|
||||||
|
@ -9,17 +9,49 @@ pub trait BatchFillable: Sized {
|
||||||
where
|
where
|
||||||
E: BatchFillableComparison + EntityTrait,
|
E: BatchFillableComparison + EntityTrait,
|
||||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
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?
|
// TODO 3 iterations... can we make it in less passes?
|
||||||
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
|
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
|
||||||
where
|
where
|
||||||
E: BatchFillableComparison + EntityTrait,
|
E: BatchFillableComparison + EntityTrait,
|
||||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
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 ids : Vec<i64> = self.iter().filter_map(|x| Some(x.object.as_ref()?.internal)).collect();
|
||||||
let batch = E::find()
|
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>
|
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
|
||||||
where
|
where
|
||||||
E: BatchFillableComparison + EntityTrait,
|
E: BatchFillableComparison + EntityTrait,
|
||||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
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 {
|
if let Some(ref obj) = self.object {
|
||||||
let batch =E::find()
|
let batch =E::find()
|
||||||
|
@ -69,7 +101,7 @@ impl BatchFillable for RichActivity {
|
||||||
mod hell {
|
mod hell {
|
||||||
use sea_orm::{sea_query::IntoCondition, ColumnTrait, ConnectionTrait, DbErr, EntityTrait};
|
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 {
|
pub trait BatchFillableComparison {
|
||||||
fn comparison(ids: Vec<i64>) -> sea_orm::Condition;
|
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> {
|
pub trait BatchFillableAcceptor<B> {
|
||||||
async fn accept(&mut self, batch: B, tx: &impl ConnectionTrait) -> Result<(), DbErr>;
|
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> {
|
async fn accept(&mut self, batch: Vec<crate::model::attachment::Model>, _tx: &impl ConnectionTrait) -> Result<(), DbErr> {
|
||||||
self.attachments = Some(batch);
|
self.attachments = Some(batch);
|
||||||
Ok(())
|
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> {
|
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());
|
self.hashtags = Some(batch.into_iter().map(|x| RichHashtag { hash: x }).collect());
|
||||||
Ok(())
|
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> {
|
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
|
// TODO batch load users from mentions rather than doing for loop
|
||||||
let mut mentions = Vec::new();
|
let mut mentions = Vec::new();
|
||||||
|
|
|
@ -5,8 +5,4 @@ mod query;
|
||||||
pub use query::Query;
|
pub use query::Query;
|
||||||
|
|
||||||
mod rich;
|
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(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 {
|
if let Some(uid) = my_id {
|
||||||
select = select
|
select = select
|
||||||
.join(
|
.join(
|
||||||
|
|
|
@ -32,23 +32,70 @@ impl IntoActivityPub for RichHashtag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RichActivity {
|
pub struct RichObject {
|
||||||
pub activity: Option<crate::model::activity::Model>,
|
|
||||||
pub object: Option<crate::model::object::Model>,
|
pub object: Option<crate::model::object::Model>,
|
||||||
pub liked: Option<i64>,
|
pub liked: Option<i64>,
|
||||||
pub attachments: Option<Vec<crate::model::attachment::Model>>,
|
pub attachments: Option<Vec<crate::model::attachment::Model>>,
|
||||||
pub hashtags: Option<Vec<RichHashtag>>,
|
pub hashtags: Option<Vec<RichHashtag>>,
|
||||||
pub mentions: Option<Vec<RichMention>>,
|
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>,
|
pub discovered: chrono::DateTime<chrono::Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromQueryResult for RichActivity {
|
impl FromQueryResult for RichActivity {
|
||||||
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
|
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
|
||||||
Ok(RichActivity {
|
Ok(RichActivity {
|
||||||
attachments: None, hashtags: None, mentions: None,
|
object: RichObject::from_query_result_optional(res, _pre)?,
|
||||||
liked: res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok(),
|
activity: crate::model::activity::Model::from_query_result_optional(res, crate::model::activity::Entity.table_name())?,
|
||||||
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(),
|
|
||||||
discovered: res.try_get(
|
discovered: res.try_get(
|
||||||
crate::model::addressing::Entity.table_name(),
|
crate::model::addressing::Entity.table_name(),
|
||||||
&crate::model::addressing::Column::Published.to_string()
|
&crate::model::addressing::Column::Published.to_string()
|
||||||
|
@ -63,76 +110,18 @@ impl IntoActivityPub for RichActivity {
|
||||||
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) => activity.into_activity_pub_json(ctx),
|
||||||
let obj = apb::Node::maybe_link(activity.object.clone());
|
|
||||||
activity.into_activity_pub_json(ctx).set_object(obj)
|
|
||||||
},
|
|
||||||
|
|
||||||
(maybe_activity, Some(object)) => {
|
(None, Some(object)) => apb::new()
|
||||||
let mut tags = Vec::new();
|
.set_activity_type(Some(apb::ActivityType::View))
|
||||||
if let Some(mentions) = self.mentions {
|
.set_published(Some(self.discovered))
|
||||||
for mention in mentions {
|
.set_object(apb::Node::object(object.into_activity_pub_json(ctx))),
|
||||||
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), Some(object)) => activity
|
||||||
Some(activity) => activity.into_activity_pub_json(ctx),
|
.into_activity_pub_json(ctx)
|
||||||
None => apb::new()
|
.set_object(apb::Node::object(object.into_activity_pub_json(ctx))),
|
||||||
.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()
|
|
||||||
),
|
|
||||||
})
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 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 {
|
pub struct RichNotification {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use axum::extract::{Path, Query, State};
|
use axum::extract::{Path, Query, State};
|
||||||
use sea_orm::{ColumnTrait, Order, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect};
|
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};
|
use crate::{activitypub::Pagination, builders::JsonLD, AuthIdentity};
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ pub async fn page(
|
||||||
.order_by(model::object::Column::Published, Order::Asc)
|
.order_by(model::object::Column::Published, Order::Asc)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.into_model::<RichActivity>()
|
.into_model::<RichObject>()
|
||||||
.all(ctx.db())
|
.all(ctx.db())
|
||||||
.await?
|
.await?
|
||||||
.with_batched::<upub::model::attachment::Entity>(ctx.db())
|
.with_batched::<upub::model::attachment::Entity>(ctx.db())
|
||||||
|
|
Loading…
Reference in a new issue