refactoring

This commit is contained in:
Felix Ableitner 2023-11-28 14:22:59 +01:00
parent c958c26257
commit 80ad6aa8d6
5 changed files with 99 additions and 86 deletions

View File

@ -1,10 +1,9 @@
use crate::database::DatabaseHandle; use crate::database::{DatabaseHandle, DbConflict};
use crate::error::{Error, MyResult}; use crate::error::MyResult;
use crate::federation::activities::create_article::CreateArticle; use crate::federation::activities::create_article::CreateArticle;
use crate::federation::activities::update_local_article::UpdateLocalArticle; use crate::federation::activities::submit_article_update;
use crate::federation::activities::update_remote_article::UpdateRemoteArticle;
use crate::federation::objects::article::DbArticle; use crate::federation::objects::article::DbArticle;
use crate::federation::objects::edit::{DbEdit, EditVersion}; use crate::federation::objects::edit::EditVersion;
use crate::federation::objects::instance::DbInstance; use crate::federation::objects::instance::DbInstance;
use crate::utils::generate_article_version; use crate::utils::generate_article_version;
use activitypub_federation::config::Data; use activitypub_federation::config::Data;
@ -14,7 +13,7 @@ use axum::extract::Query;
use axum::routing::{get, post}; use axum::routing::{get, post};
use axum::{Form, Json, Router}; use axum::{Form, Json, Router};
use axum_macros::debug_handler; use axum_macros::debug_handler;
use diffy::{apply, create_patch, merge, Patch}; use diffy::create_patch;
use futures::future::try_join_all; use futures::future::try_join_all;
use rand::random; use rand::random;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -85,54 +84,6 @@ pub struct ApiConflict {
pub previous_version: EditVersion, pub previous_version: EditVersion,
} }
#[derive(Clone, Debug)]
pub struct DbConflict {
pub id: i32,
pub diff: String,
pub article_id: ObjectId<DbArticle>,
pub previous_version: EditVersion,
}
impl DbConflict {
pub async fn to_api_conflict(
&self,
data: &Data<DatabaseHandle>,
) -> 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()
};
// create common ancestor version
let ancestor = generate_article_version(&original_article.edits, &self.previous_version)?;
let patch = Patch::from_str(&self.diff)?;
// apply self.diff to ancestor to get `ours`
let ours = apply(&ancestor, &patch)?;
match merge(&ancestor, &ours, &original_article.text) {
Ok(new_text) => {
// patch applies cleanly so we are done
// federate the change
submit_article_update(data, new_text, &original_article).await?;
// remove conflict from db
let mut lock = data.conflicts.lock().unwrap();
lock.retain(|c| c.id != self.id);
Ok(None)
}
Err(three_way_merge) => {
// there is a merge conflict, user needs to do three-way-merge
Ok(Some(ApiConflict {
id: self.id,
three_way_merge,
article_id: original_article.ap_id.clone(),
previous_version: original_article.latest_version,
}))
}
}
}
}
#[debug_handler] #[debug_handler]
async fn edit_article( async fn edit_article(
data: Data<DatabaseHandle>, data: Data<DatabaseHandle>,
@ -177,34 +128,6 @@ async fn edit_article(
} }
} }
async fn submit_article_update(
data: &Data<DatabaseHandle>,
new_text: String,
original_article: &DbArticle,
) -> Result<(), Error> {
let edit = DbEdit::new(original_article, &new_text)?;
if original_article.local {
let updated_article = {
let mut lock = data.articles.lock().unwrap();
let article = lock.get_mut(original_article.ap_id.inner()).unwrap();
article.text = new_text;
article.latest_version = edit.version.clone();
article.edits.push(edit.clone());
article.clone()
};
UpdateLocalArticle::send(updated_article, vec![], data).await?;
} else {
UpdateRemoteArticle::send(
edit,
original_article.instance.dereference(data).await?,
data,
)
.await?;
}
Ok(())
}
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone)]
pub struct GetArticleData { pub struct GetArticleData {
pub title: String, pub title: String,

View File

@ -1,7 +1,13 @@
use crate::api::DbConflict; use crate::api::ApiConflict;
use crate::error::MyResult;
use crate::federation::activities::submit_article_update;
use crate::federation::objects::article::DbArticle; use crate::federation::objects::article::DbArticle;
use crate::federation::objects::edit::EditVersion;
use crate::federation::objects::instance::DbInstance; 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 diffy::{apply, merge, Patch};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use url::Url; use url::Url;
@ -20,3 +26,51 @@ impl Database {
lock.iter().find(|i| i.1.local).unwrap().1.clone() lock.iter().find(|i| i.1.local).unwrap().1.clone()
} }
} }
#[derive(Clone, Debug)]
pub struct DbConflict {
pub id: i32,
pub diff: String,
pub article_id: ObjectId<DbArticle>,
pub previous_version: EditVersion,
}
impl DbConflict {
pub async fn to_api_conflict(
&self,
data: &Data<DatabaseHandle>,
) -> 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()
};
// create common ancestor version
let ancestor = generate_article_version(&original_article.edits, &self.previous_version)?;
let patch = Patch::from_str(&self.diff)?;
// apply self.diff to ancestor to get `ours`
let ours = apply(&ancestor, &patch)?;
match merge(&ancestor, &ours, &original_article.text) {
Ok(new_text) => {
// patch applies cleanly so we are done
// federate the change
submit_article_update(data, new_text, &original_article).await?;
// remove conflict from db
let mut lock = data.conflicts.lock().unwrap();
lock.retain(|c| c.id != self.id);
Ok(None)
}
Err(three_way_merge) => {
// there is a merge conflict, user needs to do three-way-merge
Ok(Some(ApiConflict {
id: self.id,
three_way_merge,
article_id: original_article.ap_id.clone(),
previous_version: original_article.latest_version,
}))
}
}
}
}

View File

@ -1,6 +1,42 @@
use crate::database::DatabaseHandle;
use crate::error::Error;
use crate::federation::activities::update_local_article::UpdateLocalArticle;
use crate::federation::activities::update_remote_article::UpdateRemoteArticle;
use crate::federation::objects::article::DbArticle;
use crate::federation::objects::edit::DbEdit;
use activitypub_federation::config::Data;
pub mod accept; pub mod accept;
pub mod create_article; pub mod create_article;
pub mod follow; pub mod follow;
pub mod reject; pub mod reject;
pub mod update_local_article; pub mod update_local_article;
pub mod update_remote_article; pub mod update_remote_article;
pub async fn submit_article_update(
data: &Data<DatabaseHandle>,
new_text: String,
original_article: &DbArticle,
) -> Result<(), Error> {
let edit = DbEdit::new(original_article, &new_text)?;
if original_article.local {
let updated_article = {
let mut lock = data.articles.lock().unwrap();
let article = lock.get_mut(original_article.ap_id.inner()).unwrap();
article.text = new_text;
article.latest_version = edit.version.clone();
article.edits.push(edit.clone());
article.clone()
};
UpdateLocalArticle::send(updated_article, vec![], data).await?;
} else {
UpdateRemoteArticle::send(
edit,
original_article.instance.dereference(data).await?,
data,
)
.await?;
}
Ok(())
}

View File

@ -10,7 +10,7 @@ use activitypub_federation::{
}; };
use rand::random; use rand::random;
use crate::api::DbConflict; use crate::database::DbConflict;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;

View File

@ -11,7 +11,7 @@ use std::net::ToSocketAddrs;
use tracing::info; use tracing::info;
pub mod api; pub mod api;
mod database; pub mod database;
pub mod error; pub mod error;
pub mod federation; pub mod federation;
mod utils; mod utils;