mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 14:21:09 +00:00
main code compiling
This commit is contained in:
parent
5f58c1823c
commit
573f15b293
11 changed files with 91 additions and 119 deletions
62
src/api.rs
62
src/api.rs
|
@ -45,14 +45,9 @@ async fn create_article(
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
Form(create_article): Form<CreateArticleData>,
|
Form(create_article): Form<CreateArticleData>,
|
||||||
) -> MyResult<Json<DbArticle>> {
|
) -> MyResult<Json<DbArticle>> {
|
||||||
{
|
let existing_article = DbArticle::read_local_title(&create_article.title, &data.db_connection);
|
||||||
let articles = data.articles.lock().unwrap();
|
if existing_article.is_ok() {
|
||||||
let title_exists = articles
|
return Err(anyhow!("A local article with this title already exists").into());
|
||||||
.iter()
|
|
||||||
.any(|a| a.1.local && a.1.title == create_article.title);
|
|
||||||
if title_exists {
|
|
||||||
return Err(anyhow!("A local article with this title already exists").into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let instance_id = data.local_instance().ap_id;
|
let instance_id = data.local_instance().ap_id;
|
||||||
|
@ -81,7 +76,7 @@ async fn create_article(
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct EditArticleData {
|
pub struct EditArticleData {
|
||||||
/// Id of the article to edit
|
/// Id of the article to edit
|
||||||
pub ap_id: ObjectId<DbArticle>,
|
pub article_id: i32,
|
||||||
/// Full, new text of the article. A diff against `previous_version` is generated on the server
|
/// Full, new text of the article. A diff against `previous_version` is generated on the server
|
||||||
/// side to handle conflicts.
|
/// side to handle conflicts.
|
||||||
pub new_text: String,
|
pub new_text: String,
|
||||||
|
@ -122,11 +117,7 @@ async fn edit_article(
|
||||||
}
|
}
|
||||||
lock.retain(|c| &c.id != resolve_conflict_id);
|
lock.retain(|c| &c.id != resolve_conflict_id);
|
||||||
}
|
}
|
||||||
let original_article = {
|
let original_article = DbArticle::read(edit_form.article_id, &data.db_connection)?;
|
||||||
let lock = data.articles.lock().unwrap();
|
|
||||||
let article = lock.get(edit_form.ap_id.inner()).unwrap();
|
|
||||||
article.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
if edit_form.previous_version == original_article.latest_version {
|
if edit_form.previous_version == original_article.latest_version {
|
||||||
// No intermediate changes, simply submit new version
|
// No intermediate changes, simply submit new version
|
||||||
|
@ -155,7 +146,7 @@ async fn edit_article(
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
pub struct GetArticleData {
|
pub struct GetArticleData {
|
||||||
pub id: i32,
|
pub article_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve an article by ID. It must already be stored in the local database.
|
/// Retrieve an article by ID. It must already be stored in the local database.
|
||||||
|
@ -164,7 +155,10 @@ async fn get_article(
|
||||||
Query(query): Query<GetArticleData>,
|
Query(query): Query<GetArticleData>,
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
) -> MyResult<Json<DbArticle>> {
|
) -> MyResult<Json<DbArticle>> {
|
||||||
Ok(Json(DbArticle::read(query.id, &data.db_connection)?))
|
Ok(Json(DbArticle::read(
|
||||||
|
query.article_id,
|
||||||
|
&data.db_connection,
|
||||||
|
)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
@ -234,24 +228,16 @@ async fn edit_conflicts(data: Data<MyDataHandle>) -> MyResult<Json<Vec<ApiConfli
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
pub struct SearchArticleData {
|
pub struct SearchArticleData {
|
||||||
pub title: String,
|
pub query: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search articles by title. For now only checks exact match.
|
/// Search articles for matching title or body text.
|
||||||
///
|
|
||||||
/// Later include partial title match and body search.
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
async fn search_article(
|
async fn search_article(
|
||||||
Query(query): Query<SearchArticleData>,
|
Query(query): Query<SearchArticleData>,
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
) -> MyResult<Json<Vec<DbArticle>>> {
|
) -> MyResult<Json<Vec<DbArticle>>> {
|
||||||
let articles = data.articles.lock().unwrap();
|
let article = DbArticle::search(&query.query, &data.db_connection)?;
|
||||||
let article = articles
|
|
||||||
.iter()
|
|
||||||
.filter(|a| a.1.title == query.title)
|
|
||||||
.map(|a| a.1)
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
Ok(Json(article))
|
Ok(Json(article))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +246,7 @@ pub struct ForkArticleData {
|
||||||
// TODO: could add optional param new_title so there is no problem with title collision
|
// TODO: could add optional param new_title so there is no problem with title collision
|
||||||
// in case local article with same title exists. however that makes it harder to discover
|
// in case local article with same title exists. however that makes it harder to discover
|
||||||
// variants of same article.
|
// variants of same article.
|
||||||
pub ap_id: ObjectId<DbArticle>,
|
pub article_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fork a remote article to local instance. This is useful if there are disagreements about
|
/// Fork a remote article to local instance. This is useful if there are disagreements about
|
||||||
|
@ -270,22 +256,14 @@ async fn fork_article(
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
Form(fork_form): Form<ForkArticleData>,
|
Form(fork_form): Form<ForkArticleData>,
|
||||||
) -> MyResult<Json<DbArticle>> {
|
) -> MyResult<Json<DbArticle>> {
|
||||||
let article = {
|
// TODO: lots of code duplicated from create_article(), can move it into helper
|
||||||
let lock = data.articles.lock().unwrap();
|
let original_article = DbArticle::read(fork_form.article_id, &data.db_connection)?;
|
||||||
let article = lock.get(fork_form.ap_id.inner()).unwrap();
|
let existing_article =
|
||||||
article.clone()
|
DbArticle::read_local_title(&original_article.title, &data.db_connection);
|
||||||
};
|
if existing_article.is_ok() {
|
||||||
if article.local {
|
return Err(anyhow!("A local article with this title already exists").into());
|
||||||
return Err(anyhow!("Cannot fork local article because there cant be multiple local articles with same title").into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let original_article = {
|
|
||||||
let lock = data.articles.lock().unwrap();
|
|
||||||
lock.get(fork_form.ap_id.inner())
|
|
||||||
.expect("article exists")
|
|
||||||
.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let instance_id = data.local_instance().ap_id;
|
let instance_id = data.local_instance().ap_id;
|
||||||
let ap_id = ObjectId::parse(&format!(
|
let ap_id = ObjectId::parse(&format!(
|
||||||
"http://{}:{}/article/{}",
|
"http://{}:{}/article/{}",
|
||||||
|
|
|
@ -8,8 +8,8 @@ use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use diesel::pg::PgConnection;
|
use diesel::pg::PgConnection;
|
||||||
use diesel::ExpressionMethods;
|
use diesel::ExpressionMethods;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
insert_into, AsChangeset, Identifiable, Insertable, QueryDsl, Queryable, RunQueryDsl,
|
insert_into, AsChangeset, BoolExpressionMethods, Identifiable, Insertable,
|
||||||
Selectable,
|
PgTextExpressionMethods, QueryDsl, Queryable, RunQueryDsl, Selectable,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
@ -23,9 +23,6 @@ pub struct DbArticle {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub ap_id: ObjectId<DbArticle>,
|
pub ap_id: ObjectId<DbArticle>,
|
||||||
pub instance_id: ObjectId<DbInstance>,
|
pub instance_id: ObjectId<DbInstance>,
|
||||||
/// List of all edits which make up this article, oldest first.
|
|
||||||
// TODO
|
|
||||||
//pub edits: Vec<DbEdit>,
|
|
||||||
pub latest_version: EditVersion,
|
pub latest_version: EditVersion,
|
||||||
pub local: bool,
|
pub local: bool,
|
||||||
}
|
}
|
||||||
|
@ -79,4 +76,34 @@ impl DbArticle {
|
||||||
.filter(article::dsl::ap_id.eq(ap_id))
|
.filter(article::dsl::ap_id.eq(ap_id))
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_local_title(title: &str, conn: &Mutex<PgConnection>) -> MyResult<DbArticle> {
|
||||||
|
let mut conn = conn.lock().unwrap();
|
||||||
|
Ok(article::table
|
||||||
|
.filter(article::dsl::title.eq(title))
|
||||||
|
.filter(article::dsl::local.eq(true))
|
||||||
|
.get_result(conn.deref_mut())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_all_local(conn: &Mutex<PgConnection>) -> MyResult<Vec<DbArticle>> {
|
||||||
|
let mut conn = conn.lock().unwrap();
|
||||||
|
Ok(article::table
|
||||||
|
.filter(article::dsl::local.eq(true))
|
||||||
|
.get_results(conn.deref_mut())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search(query: &str, conn: &Mutex<PgConnection>) -> MyResult<Vec<DbArticle>> {
|
||||||
|
let mut conn = conn.lock().unwrap();
|
||||||
|
let replaced = query
|
||||||
|
.replace('%', "\\%")
|
||||||
|
.replace('_', "\\_")
|
||||||
|
.replace(' ', "%");
|
||||||
|
Ok(article::table
|
||||||
|
.filter(
|
||||||
|
article::dsl::title
|
||||||
|
.ilike(&replaced)
|
||||||
|
.or(article::dsl::text.ilike(&replaced)),
|
||||||
|
)
|
||||||
|
.get_results(conn.deref_mut())?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,6 @@ pub type MyDataHandle = MyData;
|
||||||
|
|
||||||
pub struct FakeDatabase {
|
pub struct FakeDatabase {
|
||||||
pub instances: Mutex<HashMap<Url, DbInstance>>,
|
pub instances: Mutex<HashMap<Url, DbInstance>>,
|
||||||
// TODO: remove this
|
|
||||||
pub articles: Mutex<HashMap<Url, DbArticle>>,
|
|
||||||
pub conflicts: Mutex<Vec<DbConflict>>,
|
pub conflicts: Mutex<Vec<DbConflict>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,8 @@ pub async fn submit_article_update(
|
||||||
let form = DbEditForm::new(original_article, &new_text)?;
|
let form = DbEditForm::new(original_article, &new_text)?;
|
||||||
let edit = DbEdit::create(&form, &data.db_connection)?;
|
let edit = DbEdit::create(&form, &data.db_connection)?;
|
||||||
if original_article.local {
|
if original_article.local {
|
||||||
let updated_article = {
|
let updated_article =
|
||||||
let mut lock = data.articles.lock().unwrap();
|
DbArticle::update_text(edit.article_id, &new_text, &data.db_connection)?;
|
||||||
let article = lock.get_mut(original_article.ap_id.inner()).unwrap();
|
|
||||||
article.text = new_text;
|
|
||||||
article.latest_version = edit.version.clone();
|
|
||||||
article.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
UpdateLocalArticle::send(updated_article, vec![], data).await?;
|
UpdateLocalArticle::send(updated_article, vec![], data).await?;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -74,13 +74,10 @@ impl ActivityHandler for UpdateRemoteArticle {
|
||||||
|
|
||||||
/// Received on article origin instances
|
/// Received on article origin instances
|
||||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
let article_text = {
|
let local_article = DbArticle::read_from_ap_id(&self.object.object, &data.db_connection)?;
|
||||||
let lock = data.articles.lock().unwrap();
|
|
||||||
lock.get(self.object.object.inner()).unwrap().text.clone()
|
|
||||||
};
|
|
||||||
let patch = Patch::from_str(&self.object.content)?;
|
let patch = Patch::from_str(&self.object.content)?;
|
||||||
|
|
||||||
match apply(&article_text, &patch) {
|
match apply(&local_article.text, &patch) {
|
||||||
Ok(applied) => {
|
Ok(applied) => {
|
||||||
let edit = DbEdit::from_json(self.object.clone(), data).await?;
|
let edit = DbEdit::from_json(self.object.clone(), data).await?;
|
||||||
let article =
|
let article =
|
||||||
|
|
|
@ -35,7 +35,6 @@ pub async fn federation_config(hostname: &str) -> Result<FederationConfig<MyData
|
||||||
local_instance.ap_id.inner().clone(),
|
local_instance.ap_id.inner().clone(),
|
||||||
local_instance,
|
local_instance,
|
||||||
)])),
|
)])),
|
||||||
articles: Mutex::new(HashMap::new()),
|
|
||||||
conflicts: Mutex::new(vec![]),
|
conflicts: Mutex::new(vec![]),
|
||||||
});
|
});
|
||||||
let db_connection = Arc::new(Mutex::new(establish_db_connection()?));
|
let db_connection = Arc::new(Mutex::new(establish_db_connection()?));
|
||||||
|
|
|
@ -80,11 +80,6 @@ impl Object for DbArticle {
|
||||||
};
|
};
|
||||||
let article = DbArticle::create(&form, &data.db_connection)?;
|
let article = DbArticle::create(&form, &data.db_connection)?;
|
||||||
|
|
||||||
{
|
|
||||||
let mut lock = data.articles.lock().unwrap();
|
|
||||||
lock.insert(article.ap_id.clone().into(), article.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
json.edits.dereference(&article, data).await?;
|
json.edits.dereference(&article, data).await?;
|
||||||
|
|
||||||
Ok(article)
|
Ok(article)
|
||||||
|
|
|
@ -36,16 +36,7 @@ impl Collection for DbArticleCollection {
|
||||||
_owner: &Self::Owner,
|
_owner: &Self::Owner,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> Result<Self::Kind, Self::Error> {
|
) -> Result<Self::Kind, Self::Error> {
|
||||||
let local_articles = {
|
let local_articles = DbArticle::read_all_local(&data.db_connection)?;
|
||||||
let articles = data.articles.lock().unwrap();
|
|
||||||
articles
|
|
||||||
.iter()
|
|
||||||
.map(|a| a.1)
|
|
||||||
.filter(|a| a.local)
|
|
||||||
.clone()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
};
|
|
||||||
let articles = future::try_join_all(
|
let articles = future::try_join_all(
|
||||||
local_articles
|
local_articles
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -12,6 +12,7 @@ use activitypub_federation::traits::Object;
|
||||||
use activitypub_federation::traits::{ActivityHandler, Collection};
|
use activitypub_federation::traits::{ActivityHandler, Collection};
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
|
|
||||||
|
use crate::database::article::DbArticle;
|
||||||
use crate::federation::activities::create_article::CreateArticle;
|
use crate::federation::activities::create_article::CreateArticle;
|
||||||
use crate::federation::activities::reject::RejectEdit;
|
use crate::federation::activities::reject::RejectEdit;
|
||||||
use crate::federation::activities::update_local_article::UpdateLocalArticle;
|
use crate::federation::activities::update_local_article::UpdateLocalArticle;
|
||||||
|
@ -57,10 +58,7 @@ async fn http_get_article(
|
||||||
Path(title): Path<String>,
|
Path(title): Path<String>,
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
) -> MyResult<FederationJson<WithContext<ApubArticle>>> {
|
) -> MyResult<FederationJson<WithContext<ApubArticle>>> {
|
||||||
let article = {
|
let article = DbArticle::read_local_title(&title, &data.db_connection)?;
|
||||||
let lock = data.articles.lock().unwrap();
|
|
||||||
lock.values().find(|a| a.title == title).unwrap().clone()
|
|
||||||
};
|
|
||||||
let json = article.into_json(&data).await?;
|
let json = article.into_json(&data).await?;
|
||||||
Ok(FederationJson(WithContext::new_default(json)))
|
Ok(FederationJson(WithContext::new_default(json)))
|
||||||
}
|
}
|
||||||
|
@ -70,10 +68,7 @@ async fn http_get_article_edits(
|
||||||
Path(title): Path<String>,
|
Path(title): Path<String>,
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
) -> MyResult<FederationJson<WithContext<ApubEditCollection>>> {
|
) -> MyResult<FederationJson<WithContext<ApubEditCollection>>> {
|
||||||
let article = {
|
let article = DbArticle::read_local_title(&title, &data.db_connection)?;
|
||||||
let lock = data.articles.lock().unwrap();
|
|
||||||
lock.values().find(|a| a.title == title).unwrap().clone()
|
|
||||||
};
|
|
||||||
let json = DbEditCollection::read_local(&article, &data).await?;
|
let json = DbEditCollection::read_local(&article, &data).await?;
|
||||||
Ok(FederationJson(WithContext::new_default(json)))
|
Ok(FederationJson(WithContext::new_default(json)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
|
||||||
use fediwiki::api::{
|
use fediwiki::api::{
|
||||||
ApiConflict, CreateArticleData, EditArticleData, FollowInstance, GetArticleData, ResolveObject,
|
ApiConflict, CreateArticleData, EditArticleData, FollowInstance, GetArticleData, ResolveObject,
|
||||||
};
|
};
|
||||||
use fediwiki::database::DbArticle;
|
use fediwiki::database::article::DbArticle;
|
||||||
use fediwiki::error::MyResult;
|
use fediwiki::error::MyResult;
|
||||||
use fediwiki::federation::objects::instance::DbInstance;
|
use fediwiki::federation::objects::instance::DbInstance;
|
||||||
use fediwiki::start;
|
use fediwiki::start;
|
||||||
|
@ -76,7 +75,7 @@ pub async fn create_article(hostname: &str, title: String) -> MyResult<DbArticle
|
||||||
let article: DbArticle = post(hostname, "article", &create_form).await?;
|
let article: DbArticle = post(hostname, "article", &create_form).await?;
|
||||||
// create initial edit to ensure that conflicts are generated (there are no conflicts on empty file)
|
// create initial edit to ensure that conflicts are generated (there are no conflicts on empty file)
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: article.ap_id,
|
article_id: article.id,
|
||||||
new_text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
new_text: TEST_ARTICLE_DEFAULT_TEXT.to_string(),
|
||||||
previous_version: article.latest_version,
|
previous_version: article.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -84,10 +83,8 @@ pub async fn create_article(hostname: &str, title: String) -> MyResult<DbArticle
|
||||||
edit_article(hostname, &edit_form).await
|
edit_article(hostname, &edit_form).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_article(hostname: &str, ap_id: &ObjectId<DbArticle>) -> MyResult<DbArticle> {
|
pub async fn get_article(hostname: &str, article_id: i32) -> MyResult<DbArticle> {
|
||||||
let get_article = GetArticleData {
|
let get_article = GetArticleData { article_id };
|
||||||
ap_id: ap_id.clone(),
|
|
||||||
};
|
|
||||||
get_query::<DbArticle, _>(hostname, "article", Some(get_article.clone())).await
|
get_query::<DbArticle, _>(hostname, "article", Some(get_article.clone())).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +111,7 @@ pub async fn edit_article(hostname: &str, edit_form: &EditArticleData) -> MyResu
|
||||||
.await?;
|
.await?;
|
||||||
assert!(edit_res.is_none());
|
assert!(edit_res.is_none());
|
||||||
let get_article = GetArticleData {
|
let get_article = GetArticleData {
|
||||||
ap_id: edit_form.ap_id.clone(),
|
article_id: edit_form.article_id,
|
||||||
};
|
};
|
||||||
let updated_article: DbArticle = get_query(hostname, "article", Some(get_article)).await?;
|
let updated_article: DbArticle = get_query(hostname, "article", Some(get_article)).await?;
|
||||||
Ok(updated_article)
|
Ok(updated_article)
|
||||||
|
|
|
@ -10,7 +10,7 @@ use common::get;
|
||||||
use fediwiki::api::{
|
use fediwiki::api::{
|
||||||
ApiConflict, EditArticleData, ForkArticleData, ResolveObject, SearchArticleData,
|
ApiConflict, EditArticleData, ForkArticleData, ResolveObject, SearchArticleData,
|
||||||
};
|
};
|
||||||
use fediwiki::database::DbArticle;
|
use fediwiki::database::article::DbArticle;
|
||||||
use fediwiki::error::MyResult;
|
use fediwiki::error::MyResult;
|
||||||
use fediwiki::federation::objects::edit::ApubEdit;
|
use fediwiki::federation::objects::edit::ApubEdit;
|
||||||
use fediwiki::federation::objects::instance::DbInstance;
|
use fediwiki::federation::objects::instance::DbInstance;
|
||||||
|
@ -29,18 +29,18 @@ async fn test_create_read_and_edit_article() -> MyResult<()> {
|
||||||
assert!(create_res.local);
|
assert!(create_res.local);
|
||||||
|
|
||||||
// now article can be read
|
// now article can be read
|
||||||
let get_res = get_article(data.hostname_alpha, &create_res.ap_id).await?;
|
let get_res = get_article(data.hostname_alpha, create_res.id).await?;
|
||||||
assert_eq!(title, get_res.title);
|
assert_eq!(title, get_res.title);
|
||||||
assert_eq!(TEST_ARTICLE_DEFAULT_TEXT, get_res.text);
|
assert_eq!(TEST_ARTICLE_DEFAULT_TEXT, get_res.text);
|
||||||
assert!(get_res.local);
|
assert!(get_res.local);
|
||||||
|
|
||||||
// error on article which wasnt federated
|
// error on article which wasnt federated
|
||||||
let not_found = get_article(data.hostname_beta, &create_res.ap_id).await;
|
let not_found = get_article(data.hostname_beta, create_res.id).await;
|
||||||
assert!(not_found.is_err());
|
assert!(not_found.is_err());
|
||||||
|
|
||||||
// edit article
|
// edit article
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "Lorem Ipsum 2".to_string(),
|
new_text: "Lorem Ipsum 2".to_string(),
|
||||||
previous_version: get_res.latest_version,
|
previous_version: get_res.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -50,7 +50,7 @@ async fn test_create_read_and_edit_article() -> MyResult<()> {
|
||||||
assert_eq!(2, edit_res.edits.len());
|
assert_eq!(2, edit_res.edits.len());
|
||||||
|
|
||||||
let search_form = SearchArticleData {
|
let search_form = SearchArticleData {
|
||||||
title: title.clone(),
|
query: title.clone(),
|
||||||
};
|
};
|
||||||
let search_res: Vec<DbArticle> =
|
let search_res: Vec<DbArticle> =
|
||||||
get_query(data.hostname_alpha, "search", Some(search_form)).await?;
|
get_query(data.hostname_alpha, "search", Some(search_form)).await?;
|
||||||
|
@ -113,7 +113,7 @@ async fn test_synchronize_articles() -> MyResult<()> {
|
||||||
|
|
||||||
// edit the article
|
// edit the article
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "Lorem Ipsum 2\n".to_string(),
|
new_text: "Lorem Ipsum 2\n".to_string(),
|
||||||
previous_version: create_res.latest_version,
|
previous_version: create_res.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -121,7 +121,7 @@ async fn test_synchronize_articles() -> MyResult<()> {
|
||||||
edit_article(data.hostname_alpha, &edit_form).await?;
|
edit_article(data.hostname_alpha, &edit_form).await?;
|
||||||
|
|
||||||
// article is not yet on beta
|
// article is not yet on beta
|
||||||
let get_res = get_article(data.hostname_beta, &create_res.ap_id).await;
|
let get_res = get_article(data.hostname_beta, create_res.id).await;
|
||||||
assert!(get_res.is_err());
|
assert!(get_res.is_err());
|
||||||
|
|
||||||
// fetch alpha instance on beta, articles are also fetched automatically
|
// fetch alpha instance on beta, articles are also fetched automatically
|
||||||
|
@ -132,7 +132,7 @@ async fn test_synchronize_articles() -> MyResult<()> {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// get the article and compare
|
// get the article and compare
|
||||||
let get_res = get_article(data.hostname_beta, &create_res.ap_id).await?;
|
let get_res = get_article(data.hostname_beta, create_res.id).await?;
|
||||||
assert_eq!(create_res.ap_id, get_res.ap_id);
|
assert_eq!(create_res.ap_id, get_res.ap_id);
|
||||||
assert_eq!(title, get_res.title);
|
assert_eq!(title, get_res.title);
|
||||||
assert_eq!(2, get_res.edits.len());
|
assert_eq!(2, get_res.edits.len());
|
||||||
|
@ -156,7 +156,7 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
assert!(create_res.local);
|
assert!(create_res.local);
|
||||||
|
|
||||||
// article should be federated to alpha
|
// article should be federated to alpha
|
||||||
let get_res = get_article(data.hostname_alpha, &create_res.ap_id).await?;
|
let get_res = get_article(data.hostname_alpha, create_res.id).await?;
|
||||||
assert_eq!(create_res.title, get_res.title);
|
assert_eq!(create_res.title, get_res.title);
|
||||||
assert_eq!(1, get_res.edits.len());
|
assert_eq!(1, get_res.edits.len());
|
||||||
assert!(!get_res.local);
|
assert!(!get_res.local);
|
||||||
|
@ -164,7 +164,7 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
|
|
||||||
// edit the article
|
// edit the article
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id,
|
article_id: create_res.id,
|
||||||
new_text: "Lorem Ipsum 2".to_string(),
|
new_text: "Lorem Ipsum 2".to_string(),
|
||||||
previous_version: get_res.latest_version,
|
previous_version: get_res.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -178,7 +178,7 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
.starts_with(&edit_res.ap_id.to_string()));
|
.starts_with(&edit_res.ap_id.to_string()));
|
||||||
|
|
||||||
// edit should be federated to alpha
|
// edit should be federated to alpha
|
||||||
let get_res = get_article(data.hostname_alpha, &edit_res.ap_id).await?;
|
let get_res = get_article(data.hostname_alpha, edit_res.id).await?;
|
||||||
assert_eq!(edit_res.title, get_res.title);
|
assert_eq!(edit_res.title, get_res.title);
|
||||||
assert_eq!(edit_res.edits.len(), 2);
|
assert_eq!(edit_res.edits.len(), 2);
|
||||||
assert_eq!(edit_res.text, get_res.text);
|
assert_eq!(edit_res.text, get_res.text);
|
||||||
|
@ -201,17 +201,17 @@ async fn test_edit_remote_article() -> MyResult<()> {
|
||||||
assert!(create_res.local);
|
assert!(create_res.local);
|
||||||
|
|
||||||
// article should be federated to alpha and gamma
|
// article should be federated to alpha and gamma
|
||||||
let get_res = get_article(data.hostname_alpha, &create_res.ap_id).await?;
|
let get_res = get_article(data.hostname_alpha, create_res.id).await?;
|
||||||
assert_eq!(create_res.title, get_res.title);
|
assert_eq!(create_res.title, get_res.title);
|
||||||
assert_eq!(1, get_res.edits.len());
|
assert_eq!(1, get_res.edits.len());
|
||||||
assert!(!get_res.local);
|
assert!(!get_res.local);
|
||||||
|
|
||||||
let get_res = get_article(data.hostname_gamma, &create_res.ap_id).await?;
|
let get_res = get_article(data.hostname_gamma, create_res.id).await?;
|
||||||
assert_eq!(create_res.title, get_res.title);
|
assert_eq!(create_res.title, get_res.title);
|
||||||
assert_eq!(create_res.text, get_res.text);
|
assert_eq!(create_res.text, get_res.text);
|
||||||
|
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "Lorem Ipsum 2".to_string(),
|
new_text: "Lorem Ipsum 2".to_string(),
|
||||||
previous_version: get_res.latest_version,
|
previous_version: get_res.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -226,12 +226,12 @@ async fn test_edit_remote_article() -> MyResult<()> {
|
||||||
.starts_with(&edit_res.ap_id.to_string()));
|
.starts_with(&edit_res.ap_id.to_string()));
|
||||||
|
|
||||||
// edit should be federated to beta and gamma
|
// edit should be federated to beta and gamma
|
||||||
let get_res = get_article(data.hostname_alpha, &create_res.ap_id).await?;
|
let get_res = get_article(data.hostname_alpha, create_res.id).await?;
|
||||||
assert_eq!(edit_res.title, get_res.title);
|
assert_eq!(edit_res.title, get_res.title);
|
||||||
assert_eq!(edit_res.edits.len(), 2);
|
assert_eq!(edit_res.edits.len(), 2);
|
||||||
assert_eq!(edit_res.text, get_res.text);
|
assert_eq!(edit_res.text, get_res.text);
|
||||||
|
|
||||||
let get_res = get_article(data.hostname_gamma, &create_res.ap_id).await?;
|
let get_res = get_article(data.hostname_gamma, create_res.id).await?;
|
||||||
assert_eq!(edit_res.title, get_res.title);
|
assert_eq!(edit_res.title, get_res.title);
|
||||||
assert_eq!(edit_res.edits.len(), 2);
|
assert_eq!(edit_res.edits.len(), 2);
|
||||||
assert_eq!(edit_res.text, get_res.text);
|
assert_eq!(edit_res.text, get_res.text);
|
||||||
|
@ -252,7 +252,7 @@ async fn test_local_edit_conflict() -> MyResult<()> {
|
||||||
|
|
||||||
// one user edits article
|
// one user edits article
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "Lorem Ipsum\n".to_string(),
|
new_text: "Lorem Ipsum\n".to_string(),
|
||||||
previous_version: create_res.latest_version.clone(),
|
previous_version: create_res.latest_version.clone(),
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -263,7 +263,7 @@ async fn test_local_edit_conflict() -> MyResult<()> {
|
||||||
|
|
||||||
// another user edits article, without being aware of previous edit
|
// another user edits article, without being aware of previous edit
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "Ipsum Lorem\n".to_string(),
|
new_text: "Ipsum Lorem\n".to_string(),
|
||||||
previous_version: create_res.latest_version,
|
previous_version: create_res.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -279,7 +279,7 @@ async fn test_local_edit_conflict() -> MyResult<()> {
|
||||||
assert_eq!(conflicts[0], edit_res);
|
assert_eq!(conflicts[0], edit_res);
|
||||||
|
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "Lorem Ipsum and Ipsum Lorem\n".to_string(),
|
new_text: "Lorem Ipsum and Ipsum Lorem\n".to_string(),
|
||||||
previous_version: edit_res.previous_version,
|
previous_version: edit_res.previous_version,
|
||||||
resolve_conflict_id: Some(edit_res.id),
|
resolve_conflict_id: Some(edit_res.id),
|
||||||
|
@ -317,7 +317,7 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
|
|
||||||
// alpha edits article
|
// alpha edits article
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "Lorem Ipsum\n".to_string(),
|
new_text: "Lorem Ipsum\n".to_string(),
|
||||||
previous_version: create_res.latest_version.clone(),
|
previous_version: create_res.latest_version.clone(),
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -334,7 +334,7 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
// gamma also edits, as its not the latest version there is a conflict. local version should
|
// gamma also edits, as its not the latest version there is a conflict. local version should
|
||||||
// not be updated with this conflicting version, instead user needs to handle the conflict
|
// not be updated with this conflicting version, instead user needs to handle the conflict
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "aaaa\n".to_string(),
|
new_text: "aaaa\n".to_string(),
|
||||||
previous_version: create_res.latest_version,
|
previous_version: create_res.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -350,7 +350,7 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
|
|
||||||
// resolve the conflict
|
// resolve the conflict
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id,
|
article_id: create_res.id,
|
||||||
new_text: "aaaa\n".to_string(),
|
new_text: "aaaa\n".to_string(),
|
||||||
previous_version: conflicts[0].previous_version.clone(),
|
previous_version: conflicts[0].previous_version.clone(),
|
||||||
resolve_conflict_id: Some(conflicts[0].id),
|
resolve_conflict_id: Some(conflicts[0].id),
|
||||||
|
@ -379,7 +379,7 @@ async fn test_overlapping_edits_no_conflict() -> MyResult<()> {
|
||||||
|
|
||||||
// one user edits article
|
// one user edits article
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "my\nexample\ntext\n".to_string(),
|
new_text: "my\nexample\ntext\n".to_string(),
|
||||||
previous_version: create_res.latest_version.clone(),
|
previous_version: create_res.latest_version.clone(),
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -390,7 +390,7 @@ async fn test_overlapping_edits_no_conflict() -> MyResult<()> {
|
||||||
|
|
||||||
// another user edits article, without being aware of previous edit
|
// another user edits article, without being aware of previous edit
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
ap_id: create_res.ap_id.clone(),
|
article_id: create_res.id,
|
||||||
new_text: "some\nexample\narticle\n".to_string(),
|
new_text: "some\nexample\narticle\n".to_string(),
|
||||||
previous_version: create_res.latest_version,
|
previous_version: create_res.latest_version,
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
|
@ -427,7 +427,7 @@ async fn test_fork_article() -> MyResult<()> {
|
||||||
|
|
||||||
// fork the article to local instance
|
// fork the article to local instance
|
||||||
let fork_form = ForkArticleData {
|
let fork_form = ForkArticleData {
|
||||||
ap_id: resolved_article.ap_id.clone(),
|
article_id: resolved_article.id,
|
||||||
};
|
};
|
||||||
let fork_res: DbArticle = post(data.hostname_beta, "article/fork", &fork_form).await?;
|
let fork_res: DbArticle = post(data.hostname_beta, "article/fork", &fork_form).await?;
|
||||||
assert_eq!(resolved_article.title, fork_res.title);
|
assert_eq!(resolved_article.title, fork_res.title);
|
||||||
|
@ -442,7 +442,7 @@ async fn test_fork_article() -> MyResult<()> {
|
||||||
|
|
||||||
// now search returns two articles for this title (original and forked)
|
// now search returns two articles for this title (original and forked)
|
||||||
let search_form = SearchArticleData {
|
let search_form = SearchArticleData {
|
||||||
title: title.clone(),
|
query: title.clone(),
|
||||||
};
|
};
|
||||||
let search_res: Vec<DbArticle> =
|
let search_res: Vec<DbArticle> =
|
||||||
get_query(data.hostname_beta, "search", Some(search_form)).await?;
|
get_query(data.hostname_beta, "search", Some(search_form)).await?;
|
||||||
|
|
Loading…
Reference in a new issue