use objectid as db type instead of dburl

This commit is contained in:
Felix Ableitner 2023-11-30 13:10:42 +01:00
parent e030419cc5
commit 5f58c1823c
12 changed files with 47 additions and 142 deletions

3
Cargo.lock generated
View File

@ -5,7 +5,7 @@ version = 3
[[package]]
name = "activitypub_federation"
version = "0.5.0-beta.5"
source = "git+https://github.com/LemmyNet/activitypub-federation-rust.git?branch=parse-impl#2aa64ad1de7943840677f4b96a20a11d38e2be56"
source = "git+https://github.com/LemmyNet/activitypub-federation-rust.git?branch=diesel-feature#ca42d891b10888c0dcc666140385d380c664a978"
dependencies = [
"activitystreams-kinds",
"async-trait",
@ -14,6 +14,7 @@ dependencies = [
"bytes",
"chrono",
"derive_builder",
"diesel",
"dyn-clone",
"enum_delegate",
"futures",

View File

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
activitypub_federation = { git = "https://github.com/LemmyNet/activitypub-federation-rust.git", branch = "parse-impl", features = ["axum"], default-features = false }
activitypub_federation = { git = "https://github.com/LemmyNet/activitypub-federation-rust.git", branch = "diesel-feature", features = ["axum", "diesel"], default-features = false }
anyhow = "1.0.75"
async-trait = "0.1.74"
axum = "0.6.20"

View File

@ -1,5 +1,4 @@
use crate::database::article::{DbArticle, DbArticleForm};
use crate::database::dburl::DbUrl;
use crate::database::edit::{DbEdit, EditVersion};
use crate::database::{DbConflict, MyDataHandle};
use crate::error::MyResult;
@ -56,11 +55,11 @@ async fn create_article(
}
}
let instance_id: DbUrl = data.local_instance().ap_id.into();
let ap_id = Url::parse(&format!(
let instance_id = data.local_instance().ap_id;
let ap_id = ObjectId::parse(&format!(
"http://{}:{}/article/{}",
instance_id.domain().unwrap(),
instance_id.port().unwrap(),
instance_id.inner().domain().unwrap(),
instance_id.inner().port().unwrap(),
create_article.title
))?
.into();
@ -287,11 +286,11 @@ async fn fork_article(
.clone()
};
let instance_id: DbUrl = data.local_instance().ap_id.into();
let ap_id = Url::parse(&format!(
let instance_id = data.local_instance().ap_id;
let ap_id = ObjectId::parse(&format!(
"http://{}:{}/article/{}",
instance_id.domain().unwrap(),
instance_id.port().unwrap(),
instance_id.inner().domain().unwrap(),
instance_id.inner().port().unwrap(),
original_article.title
))?
.into();

View File

@ -1,9 +1,10 @@
use crate::database::dburl::DbUrl;
use crate::database::edit::EditVersion;
use crate::database::schema::article;
use crate::error::MyResult;
use crate::federation::objects::edits_collection::DbEditCollection;
use crate::federation::objects::instance::DbInstance;
use activitypub_federation::fetch::collection_id::CollectionId;
use activitypub_federation::fetch::object_id::ObjectId;
use diesel::pg::PgConnection;
use diesel::ExpressionMethods;
use diesel::{
@ -20,8 +21,8 @@ pub struct DbArticle {
pub id: i32,
pub title: String,
pub text: String,
pub ap_id: DbUrl,
pub instance_id: DbUrl,
pub ap_id: ObjectId<DbArticle>,
pub instance_id: ObjectId<DbInstance>,
/// List of all edits which make up this article, oldest first.
// TODO
//pub edits: Vec<DbEdit>,
@ -34,9 +35,9 @@ pub struct DbArticle {
pub struct DbArticleForm {
pub title: String,
pub text: String,
pub ap_id: DbUrl,
pub ap_id: ObjectId<DbArticle>,
// TODO: change to foreign key
pub instance_id: DbUrl,
pub instance_id: ObjectId<DbInstance>,
// TODO: instead of this we can use latest entry in edits table
pub latest_version: String,
pub local: bool,
@ -47,32 +48,35 @@ impl DbArticle {
Ok(CollectionId::parse(&format!("{}/edits", self.ap_id))?)
}
pub fn create(form: &DbArticleForm, conn: &Mutex<PgConnection>) -> MyResult<DbArticle> {
let mut conn = conn.lock().unwrap().deref_mut();
pub fn create(form: &DbArticleForm, conn: &Mutex<PgConnection>) -> MyResult<Self> {
let mut conn = conn.lock().unwrap();
Ok(insert_into(article::table)
.values(form)
.on_conflict(article::dsl::ap_id)
.do_update()
.set(form)
.get_result(conn)?)
.get_result(conn.deref_mut())?)
}
pub fn update_text(id: i32, text: &str, conn: &Mutex<PgConnection>) -> MyResult<Self> {
let mut conn = conn.lock().unwrap();
Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::text.eq(text))
.get_result::<Self>(&mut conn)?)
.get_result::<Self>(conn.deref_mut())?)
}
pub fn read(id: i32, conn: &Mutex<PgConnection>) -> MyResult<DbArticle> {
let mut conn = conn.lock().unwrap();
Ok(article::table.find(id).get_result(&mut conn)?)
Ok(article::table.find(id).get_result(conn.deref_mut())?)
}
pub fn read_from_ap_id(ap_id: &DbUrl, conn: &Mutex<PgConnection>) -> MyResult<DbArticle> {
pub fn read_from_ap_id(
ap_id: &ObjectId<DbArticle>,
conn: &Mutex<PgConnection>,
) -> MyResult<DbArticle> {
let mut conn = conn.lock().unwrap();
Ok(article::table
.filter(article::dsl::ap_id.eq(ap_id))
.get_result(&mut conn)?)
.get_result(conn.deref_mut())?)
}
}

View File

@ -1,96 +0,0 @@
use activitypub_federation::fetch::collection_id::CollectionId;
use activitypub_federation::fetch::object_id::ObjectId;
use activitypub_federation::traits::{Collection, Object};
use diesel::backend::Backend;
use diesel::deserialize::FromSql;
use diesel::pg::Pg;
use diesel::{AsExpression, FromSqlRow};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use url::Url;
/// Copied from lemmy, could be moved into common library
#[repr(transparent)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash, AsExpression, FromSqlRow)]
#[diesel(sql_type = diesel::sql_types::Text)]
pub struct DbUrl(pub(crate) Box<Url>);
// TODO: Lemmy doesnt need this, but for some reason derive fails to generate it
impl FromSql<diesel::sql_types::Text, Pg> for DbUrl {
fn from_sql(bytes: <Pg as Backend>::RawValue<'_>) -> diesel::deserialize::Result<Self> {
todo!()
}
}
impl Display for DbUrl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.clone().0.fmt(f)
}
}
// the project doesnt compile with From
#[allow(clippy::from_over_into)]
impl Into<DbUrl> for Url {
fn into(self) -> DbUrl {
DbUrl(Box::new(self))
}
}
#[allow(clippy::from_over_into)]
impl Into<Url> for DbUrl {
fn into(self) -> Url {
*self.0
}
}
impl<T> From<DbUrl> for ObjectId<T>
where
T: Object + Send + 'static,
for<'de2> <T as Object>::Kind: Deserialize<'de2>,
{
fn from(value: DbUrl) -> Self {
let url: Url = value.into();
ObjectId::from(url)
}
}
impl<T> From<DbUrl> for CollectionId<T>
where
T: Collection + Send + 'static,
for<'de2> <T as Collection>::Kind: Deserialize<'de2>,
{
fn from(value: DbUrl) -> Self {
let url: Url = value.into();
CollectionId::from(url)
}
}
impl<T> From<CollectionId<T>> for DbUrl
where
T: Collection,
for<'de2> <T as Collection>::Kind: Deserialize<'de2>,
{
fn from(value: CollectionId<T>) -> Self {
let url: Url = value.into();
url.into()
}
}
impl<T> From<ObjectId<T>> for DbUrl
where
T: Object,
for<'de2> <T as Object>::Kind: Deserialize<'de2>,
{
fn from(value: ObjectId<T>) -> Self {
let url: Url = value.into();
url.into()
}
}
impl Deref for DbUrl {
type Target = Url;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@ -1,5 +1,4 @@
use crate::database::article::DbArticle;
use crate::database::dburl::DbUrl;
use crate::database::schema::edit;
use crate::error::MyResult;
use activitypub_federation::fetch::object_id::ObjectId;
@ -12,14 +11,16 @@ use diesel_derive_newtype::DieselNewType;
use diffy::create_patch;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha224};
use std::ops::DerefMut;
use std::sync::Mutex;
use url::Url;
/// Represents a single change to the article.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Queryable, Selectable, Identifiable)]
#[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))]
pub struct DbEdit {
pub id: i32,
pub ap_id: DbUrl,
pub ap_id: ObjectId<DbEdit>,
pub diff: String,
pub article_id: i32,
pub version: EditVersion,
@ -30,7 +31,7 @@ pub struct DbEdit {
#[derive(Debug, Clone, Insertable, AsChangeset)]
#[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))]
pub struct DbEditForm {
pub ap_id: DbUrl,
pub ap_id: ObjectId<DbEdit>,
pub diff: String,
pub article_id: i32,
pub version: EditVersion,
@ -43,11 +44,11 @@ impl DbEditForm {
let mut sha224 = Sha224::new();
sha224.update(diff.to_bytes());
let hash = format!("{:X}", sha224.finalize());
let edit_id = ObjectId::parse(&format!("{}/{}", original_article.ap_id, hash))?;
let edit_id = Url::parse(&format!("{}/{}", original_article.ap_id, hash))?;
Ok(DbEditForm {
ap_id: edit_id.into(),
diff: diff.to_string(),
article_id: original_article.ap_id.clone(),
article_id: original_article.id,
version: EditVersion(hash),
local: true,
})
@ -62,7 +63,7 @@ impl DbEdit {
.on_conflict(edit::dsl::ap_id)
.do_update()
.set(form)
.get_result(&mut conn)?)
.get_result(conn.deref_mut())?)
}
pub fn for_article(id: i32, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
@ -70,7 +71,7 @@ impl DbEdit {
Ok(edit::table
.filter(edit::dsl::id.eq(id))
.order_by(edit::dsl::id.asc())
.get_results(&mut conn)?)
.get_results(conn.deref_mut())?)
}
}

View File

@ -7,7 +7,7 @@ use crate::federation::objects::instance::DbInstance;
use crate::utils::generate_article_version;
use activitypub_federation::config::Data;
use activitypub_federation::fetch::object_id::ObjectId;
use diesel::{Identifiable, PgConnection, QueryDsl};
use diesel::PgConnection;
use diffy::{apply, merge, Patch};
use edit::EditVersion;
use std::collections::HashMap;
@ -16,7 +16,6 @@ use std::sync::{Arc, Mutex};
use url::Url;
pub mod article;
pub mod dburl;
pub mod edit;
mod schema;
@ -62,11 +61,8 @@ impl DbConflict {
&self,
data: &Data<MyDataHandle>,
) -> MyResult<Option<ApiConflict>> {
let original_article = {
let mut lock = data.articles.lock().unwrap();
let article = lock.get_mut(self.article_id.inner()).unwrap();
article.clone()
};
let original_article =
DbArticle::read_from_ap_id(&self.article_id.clone().into(), &data.db_connection)?;
// create common ancestor version
let edits = DbEdit::for_article(original_article.id, &data.db_connection)?;

View File

@ -25,7 +25,7 @@ pub async fn submit_article_update(
if original_article.local {
let updated_article = {
let mut lock = data.articles.lock().unwrap();
let article = lock.get_mut(&original_article.ap_id).unwrap();
let article = lock.get_mut(original_article.ap_id.inner()).unwrap();
article.text = new_text;
article.latest_version = edit.version.clone();
article.clone()

View File

@ -84,7 +84,7 @@ impl ActivityHandler for UpdateRemoteArticle {
Ok(applied) => {
let edit = DbEdit::from_json(self.object.clone(), data).await?;
let article =
DbArticle::update_text(edit.article_id, &applied, &mut data.db_connection)?;
DbArticle::update_text(edit.article_id, &applied, &data.db_connection)?;
UpdateLocalArticle::send(article, vec![self.actor.dereference(data).await?], data)
.await?;
}

View File

@ -40,18 +40,18 @@ impl Object for DbArticle {
object_id: Url,
data: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
let article = DbArticle::read_from_ap_id(&object_id.into(), &mut data.db_connection).ok();
let article = DbArticle::read_from_ap_id(&object_id.into(), &data.db_connection).ok();
Ok(article)
}
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let instance: DbInstance = ObjectId::from(self.instance_id)
let instance: DbInstance = ObjectId::from(self.instance_id.clone())
.dereference_local(data)
.await?;
Ok(ApubArticle {
kind: Default::default(),
id: self.ap_id.clone().into(),
attributed_to: self.instance_id.clone().into(),
attributed_to: instance.ap_id.clone().into(),
to: vec![public(), instance.followers_url()?],
edits: self.edits_id()?,
latest_version: self.latest_version,
@ -78,7 +78,7 @@ impl Object for DbArticle {
local: false,
instance_id: json.attributed_to.into(),
};
let mut article = DbArticle::create(&form, &data.db_connection)?;
let article = DbArticle::create(&form, &data.db_connection)?;
{
let mut lock = data.articles.lock().unwrap();

View File

@ -39,7 +39,7 @@ impl Object for DbEdit {
}
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let article = DbArticle::read(self.article_id, &mut data.db_connection)?;
let article = DbArticle::read(self.article_id, &data.db_connection)?;
Ok(ApubEdit {
kind: EditType::Edit,
id: self.ap_id.into(),
@ -68,7 +68,7 @@ impl Object for DbEdit {
version: json.version,
local: false,
};
let edit = DbEdit::create(&form, &mut data.db_connection)?;
let edit = DbEdit::create(&form, &data.db_connection)?;
Ok(edit)
}
}

View File

@ -37,7 +37,7 @@ impl Collection for DbEditCollection {
owner: &Self::Owner,
data: &Data<Self::DataType>,
) -> Result<Self::Kind, Self::Error> {
let edits = DbEditCollection(DbEdit::for_article(owner.id, &mut data.db_connection)?);
let edits = DbEditCollection(DbEdit::for_article(owner.id, &data.db_connection)?);
let edits = future::try_join_all(
edits