fix: generic JsonVec that accepts null
not the cleanest solution but should be generic and transparent
This commit is contained in:
parent
88b87c0b20
commit
fafe5307c5
9 changed files with 119 additions and 61 deletions
|
@ -1,4 +1,4 @@
|
|||
use upub::model::{addressing, config, credential, activity, object, actor, Audience};
|
||||
use upub::{ext::JsonVec, model::{activity, actor, addressing, config, credential, object}};
|
||||
use openssl::rsa::Rsa;
|
||||
use sea_orm::{ActiveValue::NotSet, IntoActiveModel};
|
||||
|
||||
|
@ -21,8 +21,8 @@ pub async fn faker(ctx: upub::Context, count: i64) -> Result<(), sea_orm::DbErr>
|
|||
followers: None,
|
||||
followers_count: 0,
|
||||
statuses_count: count as i32,
|
||||
fields: vec![],
|
||||
also_known_as: vec![],
|
||||
fields: JsonVec::default(),
|
||||
also_known_as: JsonVec::default(),
|
||||
moved_to: None,
|
||||
icon: Some("https://cdn.alemi.dev/social/circle-square.png".to_string()),
|
||||
image: Some("https://cdn.alemi.dev/social/someriver-xs.jpg".to_string()),
|
||||
|
@ -90,10 +90,10 @@ pub async fn faker(ctx: upub::Context, count: i64) -> Result<(), sea_orm::DbErr>
|
|||
likes: Set(0),
|
||||
announces: Set(0),
|
||||
audience: Set(None),
|
||||
to: Set(Audience(vec![apb::target::PUBLIC.to_string()])),
|
||||
bto: Set(Audience::default()),
|
||||
cc: Set(Audience(vec![])),
|
||||
bcc: Set(Audience::default()),
|
||||
to: Set(JsonVec(vec![apb::target::PUBLIC.to_string()])),
|
||||
bto: Set(JsonVec::default()),
|
||||
cc: Set(JsonVec(vec![])),
|
||||
bcc: Set(JsonVec::default()),
|
||||
url: Set(None),
|
||||
sensitive: Set(false),
|
||||
}).exec(db).await?;
|
||||
|
@ -106,10 +106,10 @@ pub async fn faker(ctx: upub::Context, count: i64) -> Result<(), sea_orm::DbErr>
|
|||
object: Set(Some(format!("{domain}/objects/{oid}"))),
|
||||
target: Set(None),
|
||||
published: Set(chrono::Utc::now() - std::time::Duration::from_secs(60*i as u64)),
|
||||
to: Set(Audience(vec![apb::target::PUBLIC.to_string()])),
|
||||
bto: Set(Audience::default()),
|
||||
cc: Set(Audience(vec![])),
|
||||
bcc: Set(Audience::default()),
|
||||
to: Set(JsonVec(vec![apb::target::PUBLIC.to_string()])),
|
||||
bto: Set(JsonVec::default()),
|
||||
cc: Set(JsonVec(vec![])),
|
||||
bcc: Set(JsonVec::default()),
|
||||
}).exec(db).await?;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,3 +48,66 @@ impl<T, E: std::error::Error> LoggableError for Result<T, E> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct JsonVec<T>(pub Vec<T>);
|
||||
|
||||
impl<T> From<Vec<T>> for JsonVec<T> {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
JsonVec(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for JsonVec<T> {
|
||||
fn default() -> Self {
|
||||
JsonVec(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: serde::de::DeserializeOwned> sea_orm::TryGetableFromJson for JsonVec<T> {}
|
||||
|
||||
impl<T: serde::ser::Serialize> std::convert::From<JsonVec<T>> for sea_orm::Value {
|
||||
fn from(source: JsonVec<T>) -> Self {
|
||||
sea_orm::Value::Json(serde_json::to_value(&source).ok().map(std::boxed::Box::new))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: serde::de::DeserializeOwned + TypeName> sea_orm::sea_query::ValueType for JsonVec<T> {
|
||||
fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> {
|
||||
match v {
|
||||
sea_orm::Value::Json(Some(json)) => Ok(
|
||||
serde_json::from_value(*json).map_err(|_| sea_orm::sea_query::ValueTypeErr)?,
|
||||
),
|
||||
sea_orm::Value::Json(None) => Ok(JsonVec::default()),
|
||||
_ => Err(sea_orm::sea_query::ValueTypeErr),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_name() -> String {
|
||||
format!("JsonVec_{}", T::type_name())
|
||||
}
|
||||
|
||||
fn array_type() -> sea_orm::sea_query::ArrayType {
|
||||
sea_orm::sea_query::ArrayType::Json
|
||||
}
|
||||
|
||||
fn column_type() -> sea_orm::sea_query::ColumnType {
|
||||
sea_orm::sea_query::ColumnType::Json
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> sea_orm::sea_query::Nullable for JsonVec<T> {
|
||||
fn null() -> sea_orm::Value {
|
||||
sea_orm::Value::Json(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TypeName {
|
||||
fn type_name() -> String;
|
||||
}
|
||||
|
||||
impl TypeName for String {
|
||||
fn type_name() -> String {
|
||||
"String".to_string()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use openssl::rsa::Rsa;
|
||||
use sea_orm::{ActiveValue::{NotSet, Set}, DatabaseConnection, EntityTrait};
|
||||
|
||||
use crate::model;
|
||||
use crate::{ext::JsonVec, model};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum InitError {
|
||||
|
@ -34,9 +34,9 @@ pub async fn application(
|
|||
domain: Set(domain.clone()),
|
||||
preferred_username: Set(domain.clone()),
|
||||
actor_type: Set(apb::ActorType::Application),
|
||||
also_known_as: Set(vec![]),
|
||||
also_known_as: Set(JsonVec::default()),
|
||||
moved_to: Set(None),
|
||||
fields: Set(vec![]), // TODO we could put some useful things here actually
|
||||
fields: Set(JsonVec::default()), // TODO we could put some useful things here actually
|
||||
private_key: Set(Some(privk)),
|
||||
public_key: Set(pubk),
|
||||
following: Set(None),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use apb::{ActivityMut, ActivityType, BaseMut, ObjectMut};
|
||||
use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns};
|
||||
|
||||
use crate::model::Audience;
|
||||
use crate::ext::JsonVec;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "activities")]
|
||||
|
@ -14,10 +14,10 @@ pub struct Model {
|
|||
pub actor: String,
|
||||
pub object: Option<String>,
|
||||
pub target: Option<String>,
|
||||
pub to: Audience,
|
||||
pub bto: Audience,
|
||||
pub cc: Audience,
|
||||
pub bcc: Audience,
|
||||
pub to: JsonVec<String>,
|
||||
pub bto: JsonVec<String>,
|
||||
pub cc: JsonVec<String>,
|
||||
pub bcc: JsonVec<String>,
|
||||
pub published: ChronoDateTimeUtc,
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns};
|
|||
|
||||
use apb::{field::OptionalString, ActorMut, ActorType, BaseMut, DocumentMut, EndpointsMut, ObjectMut, PublicKeyMut};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, sea_orm::FromJsonQueryResult)]
|
||||
use crate::ext::{JsonVec, TypeName};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Field {
|
||||
pub name: String,
|
||||
pub content: String,
|
||||
|
@ -12,6 +14,12 @@ pub struct Field {
|
|||
pub field_type: String,
|
||||
}
|
||||
|
||||
impl TypeName for Field {
|
||||
fn type_name() -> String {
|
||||
"Field".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: apb::Object> From<T> for Field {
|
||||
fn from(value: T) -> Self {
|
||||
Field {
|
||||
|
@ -37,7 +45,7 @@ pub struct Model {
|
|||
pub image: Option<String>,
|
||||
pub icon: Option<String>,
|
||||
pub preferred_username: String,
|
||||
pub fields: Vec<Field>,
|
||||
pub fields: JsonVec<Field>,
|
||||
pub inbox: Option<String>,
|
||||
pub shared_inbox: Option<String>,
|
||||
pub outbox: Option<String>,
|
||||
|
@ -50,7 +58,7 @@ pub struct Model {
|
|||
pub private_key: Option<String>,
|
||||
pub published: ChronoDateTimeUtc,
|
||||
pub updated: ChronoDateTimeUtc,
|
||||
pub also_known_as: Vec<String>,
|
||||
pub also_known_as: JsonVec<String>,
|
||||
pub moved_to: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -208,7 +216,7 @@ impl Model {
|
|||
.set_url(apb::Node::link(i.clone()))
|
||||
)))
|
||||
.set_attachment(apb::Node::array(
|
||||
self.fields
|
||||
self.fields.0
|
||||
.into_iter()
|
||||
.filter_map(|x| serde_json::to_value(x).ok())
|
||||
.collect()
|
||||
|
@ -233,7 +241,7 @@ impl Model {
|
|||
apb::new()
|
||||
.set_shared_inbox(self.shared_inbox.as_deref())
|
||||
))
|
||||
.set_also_known_as(apb::Node::links(self.also_known_as))
|
||||
.set_also_known_as(apb::Node::links(self.also_known_as.0))
|
||||
.set_moved_to(apb::Node::maybe_link(self.moved_to))
|
||||
.set_discoverable(Some(true))
|
||||
}
|
||||
|
|
|
@ -19,23 +19,3 @@ pub mod dislike;
|
|||
pub mod hashtag;
|
||||
pub mod mention;
|
||||
pub mod attachment;
|
||||
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, sea_orm::FromJsonQueryResult)]
|
||||
pub struct Audience(pub Vec<String>);
|
||||
|
||||
impl<T: apb::Base> From<apb::Node<T>> for Audience {
|
||||
fn from(value: apb::Node<T>) -> Self {
|
||||
use apb::field::OptionalString;
|
||||
|
||||
Audience(
|
||||
match value {
|
||||
apb::Node::Empty => vec![],
|
||||
apb::Node::Link(l) => l.href().map(|x| vec![x.to_string()]).unwrap_or_default(),
|
||||
apb::Node::Object(o) => if let Ok(id) = o.id() { vec![id.to_string()] } else { vec![] },
|
||||
apb::Node::Array(arr) => arr.into_iter().filter_map(|l| l.id().str()).collect(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use apb::{BaseMut, CollectionMut, DocumentMut, ObjectMut, ObjectType};
|
||||
use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns};
|
||||
|
||||
use super::Audience;
|
||||
use crate::ext::JsonVec;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "objects")]
|
||||
|
@ -24,10 +24,10 @@ pub struct Model {
|
|||
pub announces: i32,
|
||||
pub replies: i32,
|
||||
pub context: Option<String>,
|
||||
pub to: Audience,
|
||||
pub bto: Audience,
|
||||
pub cc: Audience,
|
||||
pub bcc: Audience,
|
||||
pub to: JsonVec<String>,
|
||||
pub bto: JsonVec<String>,
|
||||
pub cc: JsonVec<String>,
|
||||
pub bcc: JsonVec<String>,
|
||||
pub published: ChronoDateTimeUtc,
|
||||
pub updated: ChronoDateTimeUtc,
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use sea_orm::{ActiveValue::{NotSet, Set}, DbErr, EntityTrait};
|
||||
|
||||
use crate::ext::JsonVec;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Administrable {
|
||||
async fn register_user(
|
||||
|
@ -35,13 +37,13 @@ impl Administrable for crate::Context {
|
|||
domain: Set(domain),
|
||||
summary: Set(summary),
|
||||
preferred_username: Set(username.clone()),
|
||||
fields: Set(vec![]),
|
||||
fields: Set(JsonVec::default()),
|
||||
following: Set(None),
|
||||
following_count: Set(0),
|
||||
followers: Set(None),
|
||||
followers_count: Set(0),
|
||||
statuses_count: Set(0),
|
||||
also_known_as: Set(vec![]),
|
||||
also_known_as: Set(JsonVec::default()),
|
||||
moved_to: Set(None),
|
||||
icon: Set(avatar_url),
|
||||
image: Set(banner_url),
|
||||
|
|
|
@ -203,10 +203,10 @@ impl AP {
|
|||
object: activity.object().id().str(),
|
||||
target: activity.target().id().str(),
|
||||
published: activity.published().unwrap_or(chrono::Utc::now()),
|
||||
to: activity.to().into(),
|
||||
bto: activity.bto().into(),
|
||||
cc: activity.cc().into(),
|
||||
bcc: activity.bcc().into(),
|
||||
to: activity.to().all_ids().into(),
|
||||
bto: activity.bto().all_ids().into(),
|
||||
cc: activity.cc().all_ids().into(),
|
||||
bcc: activity.bcc().all_ids().into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -287,10 +287,10 @@ impl AP {
|
|||
announces: object.shares().get()
|
||||
.map_or(0, |x| x.total_items().unwrap_or(0)) as i32,
|
||||
audience: object.audience().id().str(),
|
||||
to: object.to().into(),
|
||||
bto: object.bto().into(),
|
||||
cc: object.cc().into(),
|
||||
bcc: object.bcc().into(),
|
||||
to: object.to().all_ids().into(),
|
||||
bto: object.bto().all_ids().into(),
|
||||
cc: object.cc().all_ids().into(),
|
||||
bcc: object.bcc().all_ids().into(),
|
||||
|
||||
sensitive: object.sensitive().unwrap_or(false),
|
||||
})
|
||||
|
@ -333,13 +333,12 @@ impl AP {
|
|||
summary: actor.summary().str(),
|
||||
icon: actor.icon().get().and_then(|x| x.url().id().str()),
|
||||
image: actor.image().get().and_then(|x| x.url().id().str()),
|
||||
fields: actor.attachment().flat().into_iter().filter_map(|x| Some(crate::model::actor::Field::from(x.extract()?))).collect(),
|
||||
inbox: actor.inbox().id().str(),
|
||||
outbox: actor.outbox().id().str(),
|
||||
shared_inbox: actor.endpoints().get().and_then(|x| x.shared_inbox().str()),
|
||||
followers: actor.followers().id().str(),
|
||||
following: actor.following().id().str(),
|
||||
also_known_as: actor.also_known_as().flat().into_iter().filter_map(|x| x.id().str()).collect(),
|
||||
also_known_as: actor.also_known_as().flat().into_iter().filter_map(|x| x.id().str()).collect::<Vec<String>>().into(),
|
||||
moved_to: actor.moved_to().id().str(),
|
||||
published: actor.published().unwrap_or(chrono::Utc::now()),
|
||||
updated: chrono::Utc::now(),
|
||||
|
@ -348,6 +347,12 @@ impl AP {
|
|||
statuses_count: actor.statuses_count().unwrap_or(0) as i32,
|
||||
public_key: actor.public_key().get().ok_or(apb::FieldErr("publicKey"))?.public_key_pem().to_string(),
|
||||
private_key: None, // there's no way to transport privkey over AP json, must come from DB
|
||||
fields: actor.attachment()
|
||||
.flat()
|
||||
.into_iter()
|
||||
.filter_map(|x| Some(crate::model::actor::Field::from(x.extract()?)))
|
||||
.collect::<Vec<crate::model::actor::Field>>()
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue