mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-25 20:31:09 +00:00
get follows working
This commit is contained in:
parent
90c4fbf8e4
commit
aec05ac6b5
20 changed files with 67 additions and 83 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -5,6 +5,7 @@ version = 3
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitypub_federation"
|
name = "activitypub_federation"
|
||||||
version = "0.5.0-beta.5"
|
version = "0.5.0-beta.5"
|
||||||
|
source = "git+https://github.com/LemmyNet/activitypub-federation-rust.git?branch=diesel-feature#ffb020bcdd5004fdcba501950e6a87bc82c806ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitystreams-kinds",
|
"activitystreams-kinds",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
activitypub_federation = { path = "../lemmy/activitypub-federation-rust", features = ["axum", "diesel"], 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"
|
anyhow = "1.0.75"
|
||||||
async-trait = "0.1.74"
|
async-trait = "0.1.74"
|
||||||
axum = "0.6.20"
|
axum = "0.6.20"
|
||||||
|
|
|
@ -11,11 +11,12 @@ create table instance (
|
||||||
|
|
||||||
create table instance_follow (
|
create table instance_follow (
|
||||||
id serial primary key,
|
id serial primary key,
|
||||||
|
instance_id int REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
follower_id int REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
follower_id int REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
followed_id int REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
pending boolean not null,
|
||||||
pending boolean not null
|
unique(instance_id, follower_id)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
create table article (
|
create table article (
|
||||||
id serial primary key,
|
id serial primary key,
|
||||||
title text not null,
|
title text not null,
|
||||||
|
|
12
src/api.rs
12
src/api.rs
|
@ -129,7 +129,6 @@ async fn edit_article(
|
||||||
let ancestor =
|
let ancestor =
|
||||||
generate_article_version(&original_article.edits, &edit_form.previous_version)?;
|
generate_article_version(&original_article.edits, &edit_form.previous_version)?;
|
||||||
let patch = create_patch(&ancestor, &edit_form.new_text);
|
let patch = create_patch(&ancestor, &edit_form.new_text);
|
||||||
dbg!(&edit_form.previous_version);
|
|
||||||
|
|
||||||
let db_conflict = DbConflict {
|
let db_conflict = DbConflict {
|
||||||
id: random(),
|
id: random(),
|
||||||
|
@ -141,7 +140,7 @@ async fn edit_article(
|
||||||
let mut lock = data.conflicts.lock().unwrap();
|
let mut lock = data.conflicts.lock().unwrap();
|
||||||
lock.push(db_conflict.clone());
|
lock.push(db_conflict.clone());
|
||||||
}
|
}
|
||||||
Ok(Json(dbg!(db_conflict.to_api_conflict(&data).await)?))
|
Ok(Json(db_conflict.to_api_conflict(&data).await?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,21 +225,16 @@ async fn follow_instance(
|
||||||
/// Get a list of all unresolved edit conflicts.
|
/// Get a list of all unresolved edit conflicts.
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
async fn edit_conflicts(data: Data<MyDataHandle>) -> MyResult<Json<Vec<ApiConflict>>> {
|
async fn edit_conflicts(data: Data<MyDataHandle>) -> MyResult<Json<Vec<ApiConflict>>> {
|
||||||
dbg!("a");
|
|
||||||
let conflicts = { data.conflicts.lock().unwrap().to_vec() };
|
let conflicts = { data.conflicts.lock().unwrap().to_vec() };
|
||||||
dbg!(&conflicts);
|
|
||||||
dbg!("b");
|
|
||||||
let conflicts: Vec<ApiConflict> = try_join_all(conflicts.into_iter().map(|c| {
|
let conflicts: Vec<ApiConflict> = try_join_all(conflicts.into_iter().map(|c| {
|
||||||
let data = data.reset_request_count();
|
let data = data.reset_request_count();
|
||||||
dbg!(&c.previous_version);
|
async move { c.to_api_conflict(&data).await }
|
||||||
async move { dbg!(c.to_api_conflict(&data).await) }
|
|
||||||
}))
|
}))
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
dbg!("c");
|
Ok(Json(conflicts))
|
||||||
Ok(Json(dbg!(conflicts)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::database::edit::{DbEdit, EditVersion};
|
use crate::database::edit::{DbEdit, EditVersion};
|
||||||
use crate::database::instance::DbInstance;
|
|
||||||
use crate::database::schema::article;
|
use crate::database::schema::article;
|
||||||
use crate::error::MyResult;
|
use crate::error::MyResult;
|
||||||
use crate::federation::objects::edits_collection::DbEditCollection;
|
use crate::federation::objects::edits_collection::DbEditCollection;
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
use crate::database::article::DbArticle;
|
|
||||||
use crate::database::schema::{instance, instance_follow};
|
use crate::database::schema::{instance, instance_follow};
|
||||||
use crate::database::MyDataHandle;
|
use crate::database::MyDataHandle;
|
||||||
use crate::error::{Error, MyResult};
|
use crate::error::{Error, MyResult};
|
||||||
use crate::federation::activities::follow::Follow;
|
|
||||||
use crate::federation::objects::articles_collection::DbArticleCollection;
|
use crate::federation::objects::articles_collection::DbArticleCollection;
|
||||||
use activitypub_federation::activity_sending::SendActivityTask;
|
use activitypub_federation::activity_sending::SendActivityTask;
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use activitypub_federation::fetch::collection_id::CollectionId;
|
use activitypub_federation::fetch::collection_id::CollectionId;
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use activitypub_federation::protocol::context::WithContext;
|
use activitypub_federation::protocol::context::WithContext;
|
||||||
use activitypub_federation::traits::{ActivityHandler, Actor};
|
use activitypub_federation::traits::ActivityHandler;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::ExpressionMethods;
|
use diesel::ExpressionMethods;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
insert_into, update, AsChangeset, Identifiable, Insertable, JoinOnDsl, PgConnection, QueryDsl,
|
insert_into, AsChangeset, Identifiable, Insertable, JoinOnDsl, PgConnection, QueryDsl,
|
||||||
Queryable, RunQueryDsl, Selectable,
|
Queryable, RunQueryDsl, Selectable,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -56,7 +55,7 @@ pub struct DbInstanceForm {
|
||||||
pub struct InstanceView {
|
pub struct InstanceView {
|
||||||
pub instance: DbInstance,
|
pub instance: DbInstance,
|
||||||
pub followers: Vec<DbInstance>,
|
pub followers: Vec<DbInstance>,
|
||||||
pub followed: Vec<DbInstance>,
|
pub following: Vec<DbInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbInstance {
|
impl DbInstance {
|
||||||
|
@ -151,49 +150,54 @@ impl DbInstance {
|
||||||
pub fn read_local_view(conn: &Mutex<PgConnection>) -> MyResult<InstanceView> {
|
pub fn read_local_view(conn: &Mutex<PgConnection>) -> MyResult<InstanceView> {
|
||||||
let instance = DbInstance::read_local_instance(conn)?;
|
let instance = DbInstance::read_local_instance(conn)?;
|
||||||
let followers = DbInstance::read_followers(instance.id, conn)?;
|
let followers = DbInstance::read_followers(instance.id, conn)?;
|
||||||
let followed = DbInstance::read_followed(instance.id, conn)?;
|
let following = DbInstance::read_following(instance.id, conn)?;
|
||||||
|
|
||||||
Ok(InstanceView {
|
Ok(InstanceView {
|
||||||
instance,
|
instance,
|
||||||
followers,
|
followers,
|
||||||
followed,
|
following,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn follow(
|
pub fn follow(
|
||||||
follower_id_: i32,
|
follower_id_: i32,
|
||||||
followed_id_: i32,
|
instance_id_: i32,
|
||||||
pending_: bool,
|
pending_: bool,
|
||||||
data: &Data<MyDataHandle>,
|
data: &Data<MyDataHandle>,
|
||||||
) -> MyResult<()> {
|
) -> MyResult<()> {
|
||||||
use instance_follow::dsl::{followed_id, follower_id, pending};
|
debug_assert_ne!(follower_id_, instance_id_);
|
||||||
|
use instance_follow::dsl::{follower_id, instance_id, pending};
|
||||||
let mut conn = data.db_connection.lock().unwrap();
|
let mut conn = data.db_connection.lock().unwrap();
|
||||||
insert_into(instance_follow::table)
|
let form = (
|
||||||
.values((
|
instance_id.eq(instance_id_),
|
||||||
follower_id.eq(follower_id_),
|
follower_id.eq(follower_id_),
|
||||||
followed_id.eq(followed_id_),
|
|
||||||
pending.eq(pending_),
|
pending.eq(pending_),
|
||||||
))
|
);
|
||||||
|
dbg!(follower_id_, instance_id_, pending_);
|
||||||
|
insert_into(instance_follow::table)
|
||||||
|
.values(form)
|
||||||
|
.on_conflict((instance_id, follower_id))
|
||||||
|
.do_update()
|
||||||
|
.set(form)
|
||||||
.execute(conn.deref_mut())?;
|
.execute(conn.deref_mut())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_followers(id_: i32, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
pub fn read_followers(id_: i32, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
||||||
use instance_follow::dsl::{followed_id, id};
|
use instance_follow::dsl::{follower_id, instance_id};
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = conn.lock().unwrap();
|
||||||
Ok(instance_follow::table
|
Ok(instance_follow::table
|
||||||
.inner_join(instance::table.on(id.eq(instance::dsl::id)))
|
.inner_join(instance::table.on(follower_id.eq(instance::dsl::id)))
|
||||||
.filter(followed_id.eq(id_))
|
.filter(instance_id.eq(id_))
|
||||||
.select(instance::all_columns)
|
.select(instance::all_columns)
|
||||||
.get_results(conn.deref_mut())?)
|
.get_results(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_followed(id_: i32, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
pub fn read_following(id_: i32, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
||||||
// TODO: is this correct?
|
use instance_follow::dsl::{follower_id, instance_id};
|
||||||
use instance_follow::dsl::{follower_id, id};
|
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = conn.lock().unwrap();
|
||||||
Ok(instance_follow::table
|
Ok(instance_follow::table
|
||||||
.inner_join(instance::table.on(id.eq(instance::dsl::id)))
|
.inner_join(instance::table.on(instance_id.eq(instance::dsl::id)))
|
||||||
.filter(follower_id.eq(id_))
|
.filter(follower_id.eq(id_))
|
||||||
.select(instance::all_columns)
|
.select(instance::all_columns)
|
||||||
.get_results(conn.deref_mut())?)
|
.get_results(conn.deref_mut())?)
|
||||||
|
|
|
@ -9,11 +9,9 @@ use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use diffy::{apply, merge, Patch};
|
use diffy::{apply, merge, Patch};
|
||||||
use edit::EditVersion;
|
use edit::EditVersion;
|
||||||
use instance::DbInstance;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub mod article;
|
pub mod article;
|
||||||
pub mod edit;
|
pub mod edit;
|
||||||
|
@ -58,12 +56,10 @@ impl DbConflict {
|
||||||
// create common ancestor version
|
// create common ancestor version
|
||||||
let edits = DbEdit::for_article(&original_article, &data.db_connection)?;
|
let edits = DbEdit::for_article(&original_article, &data.db_connection)?;
|
||||||
let ancestor = generate_article_version(&edits, &self.previous_version)?;
|
let ancestor = generate_article_version(&edits, &self.previous_version)?;
|
||||||
dbg!(&ancestor, &self.previous_version);
|
|
||||||
|
|
||||||
dbg!(&self.diff);
|
|
||||||
let patch = Patch::from_str(&self.diff)?;
|
let patch = Patch::from_str(&self.diff)?;
|
||||||
// apply self.diff to ancestor to get `ours`
|
// apply self.diff to ancestor to get `ours`
|
||||||
let ours = dbg!(apply(&ancestor, &patch))?;
|
let ours = apply(&ancestor, &patch)?;
|
||||||
match merge(&ancestor, &ours, &original_article.text) {
|
match merge(&ancestor, &ours, &original_article.text) {
|
||||||
Ok(new_text) => {
|
Ok(new_text) => {
|
||||||
// patch applies cleanly so we are done
|
// patch applies cleanly so we are done
|
||||||
|
|
|
@ -42,8 +42,8 @@ diesel::table! {
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
instance_follow (id) {
|
instance_follow (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
instance_id -> Int4,
|
||||||
follower_id -> Int4,
|
follower_id -> Int4,
|
||||||
followed_id -> Int4,
|
|
||||||
pending -> Bool,
|
pending -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ impl ActivityHandler for Accept {
|
||||||
// add to follows
|
// add to follows
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
||||||
let actor = self.actor.dereference(data).await?;
|
let actor = self.actor.dereference(data).await?;
|
||||||
DbInstance::follow(local_instance.id, actor.id, false, &data)?;
|
DbInstance::follow(local_instance.id, actor.id, false, data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl CreateArticle {
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
||||||
let object = article.clone().into_json(data).await?;
|
let object = article.clone().into_json(data).await?;
|
||||||
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
||||||
let to = local_instance.follower_ids(&data)?;
|
let to = local_instance.follower_ids(data)?;
|
||||||
let create = CreateArticle {
|
let create = CreateArticle {
|
||||||
actor: local_instance.ap_id.clone(),
|
actor: local_instance.ap_id.clone(),
|
||||||
to,
|
to,
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl ActivityHandler for Follow {
|
||||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
let actor = self.actor.dereference(data).await?;
|
let actor = self.actor.dereference(data).await?;
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
||||||
DbInstance::follow(actor.id, local_instance.id, false, &data)?;
|
DbInstance::follow(actor.id, local_instance.id, false, data)?;
|
||||||
|
|
||||||
// send back an accept
|
// send back an accept
|
||||||
let follower = self.actor.dereference(data).await?;
|
let follower = self.actor.dereference(data).await?;
|
||||||
|
|
|
@ -66,7 +66,6 @@ impl ActivityHandler for RejectEdit {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
dbg!(&self);
|
|
||||||
// cant convert this to DbEdit as it tries to apply patch and fails
|
// cant convert this to DbEdit as it tries to apply patch and fails
|
||||||
let mut lock = data.conflicts.lock().unwrap();
|
let mut lock = data.conflicts.lock().unwrap();
|
||||||
let conflict = DbConflict {
|
let conflict = DbConflict {
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl UpdateLocalArticle {
|
||||||
debug_assert!(article.local);
|
debug_assert!(article.local);
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
||||||
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
||||||
let mut to = local_instance.follower_ids(&data)?;
|
let mut to = local_instance.follower_ids(data)?;
|
||||||
to.extend(extra_recipients.iter().map(|i| i.ap_id.inner().clone()));
|
to.extend(extra_recipients.iter().map(|i| i.ap_id.inner().clone()));
|
||||||
let update = UpdateLocalArticle {
|
let update = UpdateLocalArticle {
|
||||||
actor: local_instance.ap_id.clone(),
|
actor: local_instance.ap_id.clone(),
|
||||||
|
|
|
@ -47,9 +47,6 @@ impl UpdateRemoteArticle {
|
||||||
kind: Default::default(),
|
kind: Default::default(),
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
// TODO: this is wrong and causes test failure. need to take previous_version from api param,
|
|
||||||
// or put previous_version in DbEdit
|
|
||||||
dbg!(&update.object.previous_version);
|
|
||||||
local_instance
|
local_instance
|
||||||
.send(update, vec![Url::parse(&article_instance.inbox_url)?], data)
|
.send(update, vec![Url::parse(&article_instance.inbox_url)?], data)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::database::article::{ArticleView, DbArticle};
|
use crate::database::article::DbArticle;
|
||||||
use crate::database::MyDataHandle;
|
use crate::database::MyDataHandle;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::federation::objects::edit::ApubEdit;
|
use crate::federation::objects::edit::ApubEdit;
|
||||||
|
|
|
@ -8,12 +8,12 @@ use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
fetch::object_id::ObjectId,
|
fetch::object_id::ObjectId,
|
||||||
protocol::{public_key::PublicKey, verification::verify_domains_match},
|
protocol::{public_key::PublicKey, verification::verify_domains_match},
|
||||||
traits::{ActivityHandler, Actor, Object},
|
traits::{Actor, Object},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Local, Utc};
|
use chrono::{DateTime, Local, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use url::{ParseError, Url};
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
@ -40,7 +40,7 @@ impl Object for DbInstance {
|
||||||
object_id: Url,
|
object_id: Url,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> Result<Option<Self>, Self::Error> {
|
) -> Result<Option<Self>, Self::Error> {
|
||||||
Ok(DbInstance::read_from_ap_id(&object_id.into(), &data).ok())
|
Ok(DbInstance::read_from_ap_id(&object_id.into(), data).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
||||||
|
|
|
@ -18,7 +18,6 @@ use diesel_migrations::MigrationHarness;
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
|
|
|
@ -29,7 +29,6 @@ pub fn generate_article_version(edits: &Vec<DbEdit>, version: &EditVersion) -> M
|
||||||
return Ok(generated);
|
return Ok(generated);
|
||||||
}
|
}
|
||||||
for e in edits {
|
for e in edits {
|
||||||
dbg!(&e);
|
|
||||||
let patch = Patch::from_str(&e.diff)?;
|
let patch = Patch::from_str(&e.diff)?;
|
||||||
generated = apply(&generated, &patch)?;
|
generated = apply(&generated, &patch)?;
|
||||||
if &e.version == version {
|
if &e.version == version {
|
||||||
|
|
|
@ -167,13 +167,7 @@ pub async fn edit_article_with_conflict(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn edit_article(hostname: &str, edit_form: &EditArticleData) -> MyResult<ArticleView> {
|
pub async fn edit_article(hostname: &str, edit_form: &EditArticleData) -> MyResult<ArticleView> {
|
||||||
let edit_res: Option<ApiConflict> = CLIENT
|
let edit_res = edit_article_with_conflict(hostname, edit_form).await?;
|
||||||
.patch(format!("http://{}/api/v1/article", hostname))
|
|
||||||
.form(&edit_form)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json()
|
|
||||||
.await?;
|
|
||||||
assert!(edit_res.is_none());
|
assert!(edit_res.is_none());
|
||||||
get_article(hostname, edit_form.article_id).await
|
get_article(hostname, edit_form.article_id).await
|
||||||
}
|
}
|
||||||
|
@ -211,13 +205,13 @@ where
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn follow_instance(follow_instance: &str, followed_instance: &str) -> MyResult<()> {
|
pub async fn follow_instance(api_instance: &str, follow_instance: &str) -> MyResult<()> {
|
||||||
// fetch beta instance on alpha
|
// fetch beta instance on alpha
|
||||||
let resolve_form = ResolveObject {
|
let resolve_form = ResolveObject {
|
||||||
id: Url::parse(&format!("http://{}", followed_instance))?,
|
id: Url::parse(&format!("http://{}", follow_instance))?,
|
||||||
};
|
};
|
||||||
let instance_resolved: DbInstance =
|
let instance_resolved: DbInstance =
|
||||||
get_query(followed_instance, "resolve_instance", Some(resolve_form)).await?;
|
get_query(api_instance, "resolve_instance", Some(resolve_form)).await?;
|
||||||
|
|
||||||
// send follow
|
// send follow
|
||||||
let follow_form = FollowInstance {
|
let follow_form = FollowInstance {
|
||||||
|
@ -225,7 +219,7 @@ pub async fn follow_instance(follow_instance: &str, followed_instance: &str) ->
|
||||||
};
|
};
|
||||||
// cant use post helper because follow doesnt return json
|
// cant use post helper because follow doesnt return json
|
||||||
CLIENT
|
CLIENT
|
||||||
.post(format!("http://{}/api/v1/instance/follow", follow_instance))
|
.post(format!("http://{}/api/v1/instance/follow", api_instance))
|
||||||
.form(&follow_form)
|
.form(&follow_form)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -83,19 +83,29 @@ async fn test_follow_instance() -> MyResult<()> {
|
||||||
// check initial state
|
// check initial state
|
||||||
let alpha_instance: InstanceView = get(&data.alpha.hostname, "instance").await?;
|
let alpha_instance: InstanceView = get(&data.alpha.hostname, "instance").await?;
|
||||||
assert_eq!(0, alpha_instance.followers.len());
|
assert_eq!(0, alpha_instance.followers.len());
|
||||||
assert_eq!(0, alpha_instance.followed.len());
|
assert_eq!(0, alpha_instance.following.len());
|
||||||
let beta_instance: InstanceView = get(&data.beta.hostname, "instance").await?;
|
let beta_instance: InstanceView = get(&data.beta.hostname, "instance").await?;
|
||||||
assert_eq!(0, beta_instance.followers.len());
|
assert_eq!(0, beta_instance.followers.len());
|
||||||
assert_eq!(0, beta_instance.followed.len());
|
assert_eq!(0, beta_instance.following.len());
|
||||||
|
|
||||||
follow_instance(&data.alpha.hostname, &data.beta.hostname).await?;
|
follow_instance(&data.alpha.hostname, &data.beta.hostname).await?;
|
||||||
|
|
||||||
// check that follow was federated
|
// check that follow was federated
|
||||||
let beta_instance: InstanceView = get(&data.beta.hostname, "instance").await?;
|
|
||||||
assert_eq!(1, beta_instance.followers.len());
|
|
||||||
|
|
||||||
let alpha_instance: InstanceView = get(&data.alpha.hostname, "instance").await?;
|
let alpha_instance: InstanceView = get(&data.alpha.hostname, "instance").await?;
|
||||||
assert_eq!(1, alpha_instance.followed.len());
|
assert_eq!(1, alpha_instance.following.len());
|
||||||
|
assert_eq!(0, alpha_instance.followers.len());
|
||||||
|
assert_eq!(
|
||||||
|
beta_instance.instance.ap_id,
|
||||||
|
alpha_instance.following[0].ap_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let beta_instance: InstanceView = get(&data.beta.hostname, "instance").await?;
|
||||||
|
assert_eq!(0, beta_instance.following.len());
|
||||||
|
assert_eq!(1, beta_instance.followers.len());
|
||||||
|
assert_eq!(
|
||||||
|
alpha_instance.instance.ap_id,
|
||||||
|
beta_instance.followers[0].ap_id
|
||||||
|
);
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
}
|
}
|
||||||
|
@ -157,7 +167,6 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
let create_res = create_article(&data.beta.hostname, title.clone()).await?;
|
let create_res = create_article(&data.beta.hostname, title.clone()).await?;
|
||||||
assert_eq!(title, create_res.article.title);
|
assert_eq!(title, create_res.article.title);
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
dbg!(1);
|
|
||||||
|
|
||||||
// article should be federated to alpha
|
// article should be federated to alpha
|
||||||
let get_res = get_article(&data.alpha.hostname, create_res.article.id).await?;
|
let get_res = get_article(&data.alpha.hostname, create_res.article.id).await?;
|
||||||
|
@ -165,7 +174,6 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
assert_eq!(1, get_res.edits.len());
|
assert_eq!(1, get_res.edits.len());
|
||||||
assert!(!get_res.article.local);
|
assert!(!get_res.article.local);
|
||||||
assert_eq!(create_res.article.text, get_res.article.text);
|
assert_eq!(create_res.article.text, get_res.article.text);
|
||||||
dbg!(2);
|
|
||||||
|
|
||||||
// edit the article
|
// edit the article
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
|
@ -175,7 +183,6 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
};
|
};
|
||||||
let edit_res = edit_article(&data.beta.hostname, &edit_form).await?;
|
let edit_res = edit_article(&data.beta.hostname, &edit_form).await?;
|
||||||
dbg!(3);
|
|
||||||
assert_eq!(edit_res.article.text, edit_form.new_text);
|
assert_eq!(edit_res.article.text, edit_form.new_text);
|
||||||
assert_eq!(edit_res.edits.len(), 2);
|
assert_eq!(edit_res.edits.len(), 2);
|
||||||
assert!(edit_res.edits[0]
|
assert!(edit_res.edits[0]
|
||||||
|
@ -185,7 +192,6 @@ async fn test_edit_local_article() -> MyResult<()> {
|
||||||
|
|
||||||
// edit should be federated to alpha
|
// edit should be federated to alpha
|
||||||
let get_res = get_article(&data.alpha.hostname, edit_res.article.id).await?;
|
let get_res = get_article(&data.alpha.hostname, edit_res.article.id).await?;
|
||||||
dbg!(4);
|
|
||||||
assert_eq!(edit_res.article.title, get_res.article.title);
|
assert_eq!(edit_res.article.title, get_res.article.title);
|
||||||
assert_eq!(edit_res.edits.len(), 2);
|
assert_eq!(edit_res.edits.len(), 2);
|
||||||
assert_eq!(edit_res.article.text, get_res.article.text);
|
assert_eq!(edit_res.article.text, get_res.article.text);
|
||||||
|
@ -341,20 +347,17 @@ 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
|
||||||
dbg!(&create_res.article.text, &create_res.latest_version);
|
|
||||||
let edit_form = EditArticleData {
|
let edit_form = EditArticleData {
|
||||||
article_id: create_res.article.id,
|
article_id: create_res.article.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,
|
||||||
};
|
};
|
||||||
dbg!(1);
|
|
||||||
let edit_res = edit_article(&data.gamma.hostname, &edit_form).await?;
|
let edit_res = edit_article(&data.gamma.hostname, &edit_form).await?;
|
||||||
assert_ne!(edit_form.new_text, edit_res.article.text);
|
assert_ne!(edit_form.new_text, edit_res.article.text);
|
||||||
assert_eq!(2, edit_res.edits.len());
|
assert_eq!(2, edit_res.edits.len());
|
||||||
assert!(!edit_res.article.local);
|
assert!(!edit_res.article.local);
|
||||||
|
|
||||||
dbg!(2);
|
|
||||||
let conflicts: Vec<ApiConflict> =
|
let conflicts: Vec<ApiConflict> =
|
||||||
get_query(&data.gamma.hostname, "edit_conflicts", None::<()>).await?;
|
get_query(&data.gamma.hostname, "edit_conflicts", None::<()>).await?;
|
||||||
assert_eq!(1, conflicts.len());
|
assert_eq!(1, conflicts.len());
|
||||||
|
@ -366,16 +369,13 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
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),
|
||||||
};
|
};
|
||||||
dbg!(3);
|
|
||||||
let edit_res = edit_article(&data.gamma.hostname, &edit_form).await?;
|
let edit_res = edit_article(&data.gamma.hostname, &edit_form).await?;
|
||||||
assert_eq!(edit_form.new_text, edit_res.article.text);
|
assert_eq!(edit_form.new_text, edit_res.article.text);
|
||||||
assert_eq!(3, edit_res.edits.len());
|
assert_eq!(3, edit_res.edits.len());
|
||||||
|
|
||||||
dbg!(4);
|
|
||||||
let conflicts: Vec<ApiConflict> =
|
let conflicts: Vec<ApiConflict> =
|
||||||
get_query(&data.gamma.hostname, "edit_conflicts", None::<()>).await?;
|
get_query(&data.gamma.hostname, "edit_conflicts", None::<()>).await?;
|
||||||
assert_eq!(0, conflicts.len());
|
assert_eq!(0, conflicts.len());
|
||||||
dbg!(5);
|
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue