mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 09:21:09 +00:00
move all api calls to ApiClient
This commit is contained in:
parent
173249ea8e
commit
9f4d20494e
6 changed files with 86 additions and 112 deletions
|
@ -111,7 +111,10 @@ pub(in crate::backend::api) async fn my_profile(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
pub(in crate::backend::api) async fn logout_user(jar: CookieJar) -> MyResult<CookieJar> {
|
pub(in crate::backend::api) async fn logout_user(
|
||||||
let jar = jar.remove(Cookie::named(AUTH_COOKIE));
|
data: Data<MyDataHandle>,
|
||||||
|
jar: CookieJar,
|
||||||
|
) -> MyResult<CookieJar> {
|
||||||
|
let jar = jar.remove(create_cookie(String::new(), &data));
|
||||||
Ok(jar)
|
Ok(jar)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::backend::api::article::{CreateArticleData, EditArticleData};
|
use crate::backend::api::article::{CreateArticleData, EditArticleData, ForkArticleData};
|
||||||
use crate::backend::api::instance::FollowInstance;
|
use crate::backend::api::instance::FollowInstance;
|
||||||
use crate::backend::api::{ResolveObject, SearchArticleData};
|
use crate::backend::api::{ResolveObject, SearchArticleData};
|
||||||
use crate::backend::database::conflict::ApiConflict;
|
use crate::backend::database::conflict::ApiConflict;
|
||||||
|
@ -17,8 +17,7 @@ pub static CLIENT: Lazy<Client> = Lazy::new(Client::new);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ApiClient {
|
pub struct ApiClient {
|
||||||
// TODO: make these private
|
client: Client,
|
||||||
pub client: Client,
|
|
||||||
pub hostname: String,
|
pub hostname: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,9 +138,50 @@ impl ApiClient {
|
||||||
Err(anyhow!("API error: {}", res.text().await?).into())
|
Err(anyhow!("API error: {}", res.text().await?).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn my_profile(&self) -> MyResult<LocalUserView> {
|
||||||
|
let req = self.client.get(format!(
|
||||||
|
"http://{}/api/v1/account/my_profile",
|
||||||
|
self.hostname
|
||||||
|
));
|
||||||
|
handle_json_res::<LocalUserView>(req).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn logout(&self) -> MyResult<()> {
|
||||||
|
self.client
|
||||||
|
.get(format!("http://{}/api/v1/account/logout", self.hostname))
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fork_article(&self, form: &ForkArticleData) -> MyResult<ArticleView> {
|
||||||
|
let req = self
|
||||||
|
.client
|
||||||
|
.post(format!("http://{}/api/v1/article/fork", self.hostname))
|
||||||
|
.form(form);
|
||||||
|
Ok(handle_json_res(req).await.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_conflicts(&self) -> MyResult<Vec<ApiConflict>> {
|
||||||
|
let req = self
|
||||||
|
.client
|
||||||
|
.get(format!("http://{}/api/v1/edit_conflicts", &self.hostname));
|
||||||
|
Ok(handle_json_res(req).await.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_article(&self, id: Url) -> MyResult<ArticleView> {
|
||||||
|
let resolve_object = ResolveObject { id };
|
||||||
|
get_query(&self.hostname, "article/resolve", Some(resolve_object)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_instance(&self, id: Url) -> MyResult<DbInstance> {
|
||||||
|
let resolve_object = ResolveObject { id };
|
||||||
|
get_query(&self.hostname, "instance/resolve", Some(resolve_object)).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_query<T, R>(hostname: &str, endpoint: &str, query: Option<R>) -> MyResult<T>
|
async fn get_query<T, R>(hostname: &str, endpoint: &str, query: Option<R>) -> MyResult<T>
|
||||||
where
|
where
|
||||||
T: for<'de> Deserialize<'de>,
|
T: for<'de> Deserialize<'de>,
|
||||||
R: Serialize,
|
R: Serialize,
|
||||||
|
@ -153,7 +193,7 @@ where
|
||||||
handle_json_res::<T>(req).await
|
handle_json_res::<T>(req).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_json_res<T>(req: RequestBuilder) -> MyResult<T>
|
async fn handle_json_res<T>(req: RequestBuilder) -> MyResult<T>
|
||||||
where
|
where
|
||||||
T: for<'de> Deserialize<'de>,
|
T: for<'de> Deserialize<'de>,
|
||||||
{
|
{
|
||||||
|
@ -166,18 +206,3 @@ where
|
||||||
Err(anyhow!("API error: {text}").into())
|
Err(anyhow!("API error: {text}").into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cover in integration test
|
|
||||||
pub async fn my_profile(hostname: &str) -> MyResult<LocalUserView> {
|
|
||||||
let req = CLIENT.get(format!("http://{}/api/v1/account/my_profile", hostname));
|
|
||||||
handle_json_res::<LocalUserView>(req).await
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: cover in integration test
|
|
||||||
pub async fn logout(hostname: &str) -> MyResult<()> {
|
|
||||||
CLIENT
|
|
||||||
.get(format!("http://{}/api/v1/account/logout", hostname))
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::common::LocalUserView;
|
use crate::common::LocalUserView;
|
||||||
use crate::frontend::api::{my_profile, ApiClient};
|
use crate::frontend::api::ApiClient;
|
||||||
use crate::frontend::components::nav::Nav;
|
use crate::frontend::components::nav::Nav;
|
||||||
use crate::frontend::pages::article::Article;
|
use crate::frontend::pages::article::Article;
|
||||||
use crate::frontend::pages::login::Login;
|
use crate::frontend::pages::login::Login;
|
||||||
|
@ -40,11 +40,10 @@ impl GlobalState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_my_profile(&self) {
|
pub fn update_my_profile(&self) {
|
||||||
let backend_hostname_ = self.backend_hostname.clone();
|
|
||||||
create_local_resource(
|
create_local_resource(
|
||||||
move || backend_hostname_.clone(),
|
move || (),
|
||||||
|backend_hostname| async move {
|
|_| async move {
|
||||||
if let Ok(my_profile) = my_profile(&backend_hostname).await {
|
if let Ok(my_profile) = GlobalState::api_client().my_profile().await {
|
||||||
expect_context::<RwSignal<GlobalState>>()
|
expect_context::<RwSignal<GlobalState>>()
|
||||||
.update(|state| state.my_profile = Some(my_profile.clone()))
|
.update(|state| state.my_profile = Some(my_profile.clone()))
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::frontend::api::logout;
|
|
||||||
use crate::frontend::app::GlobalState;
|
use crate::frontend::app::GlobalState;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos::{component, use_context, view, IntoView, RwSignal, SignalWith};
|
use leptos::{component, use_context, view, IntoView, RwSignal, SignalWith};
|
||||||
|
@ -46,7 +45,7 @@ fn do_logout() {
|
||||||
dbg!("do logout");
|
dbg!("do logout");
|
||||||
create_action(move |()| async move {
|
create_action(move |()| async move {
|
||||||
dbg!("run logout action");
|
dbg!("run logout action");
|
||||||
logout(&GlobalState::read_hostname()).await.unwrap();
|
GlobalState::api_client().logout().await.unwrap();
|
||||||
expect_context::<RwSignal<GlobalState>>()
|
expect_context::<RwSignal<GlobalState>>()
|
||||||
.get()
|
.get()
|
||||||
.update_my_profile();
|
.update_my_profile();
|
||||||
|
|
|
@ -1,19 +1,12 @@
|
||||||
use anyhow::anyhow;
|
|
||||||
use ibis_lib::backend::api::article::ForkArticleData;
|
|
||||||
use ibis_lib::backend::api::instance::FollowInstance;
|
|
||||||
use ibis_lib::backend::api::ResolveObject;
|
|
||||||
use ibis_lib::backend::database::conflict::ApiConflict;
|
|
||||||
use ibis_lib::backend::database::instance::DbInstance;
|
|
||||||
use ibis_lib::backend::start;
|
use ibis_lib::backend::start;
|
||||||
use ibis_lib::common::ArticleView;
|
|
||||||
use ibis_lib::common::RegisterUserData;
|
use ibis_lib::common::RegisterUserData;
|
||||||
use ibis_lib::frontend::api::ApiClient;
|
use ibis_lib::frontend::api::ApiClient;
|
||||||
use ibis_lib::frontend::api::{get_query, handle_json_res};
|
|
||||||
use ibis_lib::frontend::error::MyResult;
|
use ibis_lib::frontend::error::MyResult;
|
||||||
|
|
||||||
use reqwest::cookie::Jar;
|
use reqwest::cookie::Jar;
|
||||||
use reqwest::{ClientBuilder, StatusCode};
|
use reqwest::ClientBuilder;
|
||||||
use serde::de::Deserialize;
|
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -25,7 +18,6 @@ use std::thread::{sleep, spawn};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::log::LevelFilter;
|
use tracing::log::LevelFilter;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub struct TestData {
|
pub struct TestData {
|
||||||
pub alpha: IbisInstance,
|
pub alpha: IbisInstance,
|
||||||
|
@ -164,27 +156,5 @@ impl Deref for IbisInstance {
|
||||||
&self.api_client
|
&self.api_client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const TEST_ARTICLE_DEFAULT_TEXT: &str = "some\nexample\ntext\n";
|
pub const TEST_ARTICLE_DEFAULT_TEXT: &str = "some\nexample\ntext\n";
|
||||||
|
|
||||||
pub async fn get_conflicts(instance: &IbisInstance) -> MyResult<Vec<ApiConflict>> {
|
|
||||||
let req = instance.api_client.client.get(format!(
|
|
||||||
"http://{}/api/v1/edit_conflicts",
|
|
||||||
&instance.api_client.hostname
|
|
||||||
));
|
|
||||||
Ok(handle_json_res(req).await.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn fork_article(
|
|
||||||
instance: &IbisInstance,
|
|
||||||
form: &ForkArticleData,
|
|
||||||
) -> MyResult<ArticleView> {
|
|
||||||
let req = instance
|
|
||||||
.api_client
|
|
||||||
.client
|
|
||||||
.post(format!(
|
|
||||||
"http://{}/api/v1/article/fork",
|
|
||||||
instance.api_client.hostname
|
|
||||||
))
|
|
||||||
.form(form);
|
|
||||||
Ok(handle_json_res(req).await.unwrap())
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,16 +2,11 @@ extern crate ibis_lib;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use crate::common::fork_article;
|
|
||||||
use crate::common::get_conflicts;
|
|
||||||
use crate::common::{TestData, TEST_ARTICLE_DEFAULT_TEXT};
|
use crate::common::{TestData, TEST_ARTICLE_DEFAULT_TEXT};
|
||||||
use ibis_lib::backend::api::article::{CreateArticleData, EditArticleData, ForkArticleData};
|
use ibis_lib::backend::api::article::{EditArticleData, ForkArticleData};
|
||||||
use ibis_lib::backend::api::{ResolveObject, SearchArticleData};
|
use ibis_lib::backend::api::SearchArticleData;
|
||||||
use ibis_lib::backend::database::instance::{DbInstance, InstanceView};
|
|
||||||
use ibis_lib::common::{ArticleView, GetArticleData};
|
use ibis_lib::common::{ArticleView, GetArticleData};
|
||||||
use ibis_lib::common::{DbArticle, LoginUserData, RegisterUserData};
|
use ibis_lib::common::{LoginUserData, RegisterUserData};
|
||||||
use ibis_lib::frontend::api::get_query;
|
|
||||||
use ibis_lib::frontend::api::handle_json_res;
|
|
||||||
use ibis_lib::frontend::error::MyResult;
|
use ibis_lib::frontend::error::MyResult;
|
||||||
use pretty_assertions::{assert_eq, assert_ne};
|
use pretty_assertions::{assert_eq, assert_ne};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -146,15 +141,10 @@ async fn test_synchronize_articles() -> MyResult<()> {
|
||||||
data.alpha.edit_article(&edit_form).await?;
|
data.alpha.edit_article(&edit_form).await?;
|
||||||
|
|
||||||
// fetch alpha instance on beta, articles are also fetched automatically
|
// fetch alpha instance on beta, articles are also fetched automatically
|
||||||
let resolve_object = ResolveObject {
|
let instance = data
|
||||||
id: Url::parse(&format!("http://{}", &data.alpha.hostname))?,
|
.beta
|
||||||
};
|
.resolve_instance(Url::parse(&format!("http://{}", &data.alpha.hostname))?)
|
||||||
let instance: DbInstance = get_query::<DbInstance, _>(
|
.await?;
|
||||||
&data.beta.hostname,
|
|
||||||
"instance/resolve",
|
|
||||||
Some(resolve_object),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut get_article_data = GetArticleData {
|
let mut get_article_data = GetArticleData {
|
||||||
title: Some(create_res.article.title),
|
title: Some(create_res.article.title),
|
||||||
|
@ -338,7 +328,7 @@ async fn test_local_edit_conflict() -> MyResult<()> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!("<<<<<<< ours\nIpsum Lorem\n||||||| original\nsome\nexample\ntext\n=======\nLorem Ipsum\n>>>>>>> theirs\n", edit_res.three_way_merge);
|
assert_eq!("<<<<<<< ours\nIpsum Lorem\n||||||| original\nsome\nexample\ntext\n=======\nLorem Ipsum\n>>>>>>> theirs\n", edit_res.three_way_merge);
|
||||||
|
|
||||||
let conflicts = get_conflicts(&data.alpha).await?;
|
let conflicts = data.alpha.get_conflicts().await?;
|
||||||
assert_eq!(1, conflicts.len());
|
assert_eq!(1, conflicts.len());
|
||||||
assert_eq!(conflicts[0], edit_res);
|
assert_eq!(conflicts[0], edit_res);
|
||||||
|
|
||||||
|
@ -351,7 +341,7 @@ async fn test_local_edit_conflict() -> MyResult<()> {
|
||||||
let edit_res = data.alpha.edit_article(&edit_form).await?;
|
let edit_res = data.alpha.edit_article(&edit_form).await?;
|
||||||
assert_eq!(edit_form.new_text, edit_res.article.text);
|
assert_eq!(edit_form.new_text, edit_res.article.text);
|
||||||
|
|
||||||
let conflicts = get_conflicts(&data.alpha).await?;
|
let conflicts = data.alpha.get_conflicts().await?;
|
||||||
assert_eq!(0, conflicts.len());
|
assert_eq!(0, conflicts.len());
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
|
@ -373,15 +363,10 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// fetch article to gamma
|
// fetch article to gamma
|
||||||
let resolve_object = ResolveObject {
|
let resolve_res: ArticleView = data
|
||||||
id: create_res.article.ap_id.inner().clone(),
|
.gamma
|
||||||
};
|
.resolve_article(create_res.article.ap_id.inner().clone())
|
||||||
let resolve_res: ArticleView = get_query(
|
.await?;
|
||||||
&data.gamma.hostname,
|
|
||||||
"article/resolve",
|
|
||||||
Some(resolve_object),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
assert_eq!(create_res.article.text, resolve_res.article.text);
|
assert_eq!(create_res.article.text, resolve_res.article.text);
|
||||||
|
|
||||||
// alpha edits article
|
// alpha edits article
|
||||||
|
@ -421,7 +406,7 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
assert_eq!(1, edit_res.edits.len());
|
assert_eq!(1, edit_res.edits.len());
|
||||||
assert!(!edit_res.article.local);
|
assert!(!edit_res.article.local);
|
||||||
|
|
||||||
let conflicts = get_conflicts(&data.gamma).await?;
|
let conflicts = data.gamma.get_conflicts().await?;
|
||||||
assert_eq!(1, conflicts.len());
|
assert_eq!(1, conflicts.len());
|
||||||
|
|
||||||
// resolve the conflict
|
// resolve the conflict
|
||||||
|
@ -435,7 +420,7 @@ async fn test_federated_edit_conflict() -> MyResult<()> {
|
||||||
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());
|
||||||
|
|
||||||
let conflicts = get_conflicts(&data.gamma).await?;
|
let conflicts = data.gamma.get_conflicts().await?;
|
||||||
assert_eq!(0, conflicts.len());
|
assert_eq!(0, conflicts.len());
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
|
@ -473,7 +458,7 @@ async fn test_overlapping_edits_no_conflict() -> MyResult<()> {
|
||||||
resolve_conflict_id: None,
|
resolve_conflict_id: None,
|
||||||
};
|
};
|
||||||
let edit_res = data.alpha.edit_article(&edit_form).await?;
|
let edit_res = data.alpha.edit_article(&edit_form).await?;
|
||||||
let conflicts = get_conflicts(&data.alpha).await?;
|
let conflicts = data.alpha.get_conflicts().await?;
|
||||||
assert_eq!(0, conflicts.len());
|
assert_eq!(0, conflicts.len());
|
||||||
assert_eq!(3, edit_res.edits.len());
|
assert_eq!(3, edit_res.edits.len());
|
||||||
assert_eq!("my\nexample\narticle\n", edit_res.article.text);
|
assert_eq!("my\nexample\narticle\n", edit_res.article.text);
|
||||||
|
@ -495,11 +480,10 @@ async fn test_fork_article() -> MyResult<()> {
|
||||||
assert!(create_res.article.local);
|
assert!(create_res.article.local);
|
||||||
|
|
||||||
// fetch on beta
|
// fetch on beta
|
||||||
let resolve_object = ResolveObject {
|
let resolve_res = data
|
||||||
id: create_res.article.ap_id.into_inner(),
|
.beta
|
||||||
};
|
.resolve_article(create_res.article.ap_id.into_inner())
|
||||||
let resolve_res: ArticleView =
|
.await?;
|
||||||
get_query(&data.beta.hostname, "article/resolve", Some(resolve_object)).await?;
|
|
||||||
let resolved_article = resolve_res.article;
|
let resolved_article = resolve_res.article;
|
||||||
assert_eq!(create_res.edits.len(), resolve_res.edits.len());
|
assert_eq!(create_res.edits.len(), resolve_res.edits.len());
|
||||||
|
|
||||||
|
@ -507,7 +491,7 @@ async fn test_fork_article() -> MyResult<()> {
|
||||||
let fork_form = ForkArticleData {
|
let fork_form = ForkArticleData {
|
||||||
article_id: resolved_article.id,
|
article_id: resolved_article.id,
|
||||||
};
|
};
|
||||||
let fork_res = fork_article(&data.beta, &fork_form).await?;
|
let fork_res = data.beta.fork_article(&fork_form).await?;
|
||||||
let forked_article = fork_res.article;
|
let forked_article = fork_res.article;
|
||||||
assert_eq!(resolved_article.title, forked_article.title);
|
assert_eq!(resolved_article.title, forked_article.title);
|
||||||
assert_eq!(resolved_article.text, forked_article.text);
|
assert_eq!(resolved_article.text, forked_article.text);
|
||||||
|
@ -556,19 +540,13 @@ async fn test_user_registration_login() -> MyResult<()> {
|
||||||
};
|
};
|
||||||
data.alpha.login(login_data).await?;
|
data.alpha.login(login_data).await?;
|
||||||
|
|
||||||
let title = "Manu_Chao".to_string();
|
let my_profile = data.alpha.my_profile().await?;
|
||||||
let create_form = CreateArticleData {
|
assert_eq!(username, my_profile.person.username);
|
||||||
title: title.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let req = data
|
data.alpha.logout().await?;
|
||||||
.alpha
|
|
||||||
.api_client
|
let my_profile_after_logout = data.alpha.my_profile().await;
|
||||||
.client
|
assert!(my_profile_after_logout.is_err());
|
||||||
.post(format!("http://{}/api/v1/article", &data.alpha.hostname))
|
|
||||||
.form(&create_form);
|
|
||||||
let create_res: ArticleView = handle_json_res(req).await?;
|
|
||||||
assert_eq!(title, create_res.article.title);
|
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue