diff --git a/src/backend/api/user.rs b/src/backend/api/user.rs index a2a4c65..9b0788a 100644 --- a/src/backend/api/user.rs +++ b/src/backend/api/user.rs @@ -111,7 +111,10 @@ pub(in crate::backend::api) async fn my_profile( } #[debug_handler] -pub(in crate::backend::api) async fn logout_user(jar: CookieJar) -> MyResult { - let jar = jar.remove(Cookie::named(AUTH_COOKIE)); +pub(in crate::backend::api) async fn logout_user( + data: Data, + jar: CookieJar, +) -> MyResult { + let jar = jar.remove(create_cookie(String::new(), &data)); Ok(jar) } diff --git a/src/frontend/api.rs b/src/frontend/api.rs index 06c08ff..d251529 100644 --- a/src/frontend/api.rs +++ b/src/frontend/api.rs @@ -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::{ResolveObject, SearchArticleData}; use crate::backend::database::conflict::ApiConflict; @@ -17,8 +17,7 @@ pub static CLIENT: Lazy = Lazy::new(Client::new); #[derive(Clone)] pub struct ApiClient { - // TODO: make these private - pub client: Client, + client: Client, pub hostname: String, } @@ -139,9 +138,50 @@ impl ApiClient { Err(anyhow!("API error: {}", res.text().await?).into()) } } + + pub async fn my_profile(&self) -> MyResult { + let req = self.client.get(format!( + "http://{}/api/v1/account/my_profile", + self.hostname + )); + handle_json_res::(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 { + 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> { + 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 { + let resolve_object = ResolveObject { id }; + get_query(&self.hostname, "article/resolve", Some(resolve_object)).await + } + + pub async fn resolve_instance(&self, id: Url) -> MyResult { + let resolve_object = ResolveObject { id }; + get_query(&self.hostname, "instance/resolve", Some(resolve_object)).await + } } -pub async fn get_query(hostname: &str, endpoint: &str, query: Option) -> MyResult +async fn get_query(hostname: &str, endpoint: &str, query: Option) -> MyResult where T: for<'de> Deserialize<'de>, R: Serialize, @@ -153,7 +193,7 @@ where handle_json_res::(req).await } -pub async fn handle_json_res(req: RequestBuilder) -> MyResult +async fn handle_json_res(req: RequestBuilder) -> MyResult where T: for<'de> Deserialize<'de>, { @@ -166,18 +206,3 @@ where Err(anyhow!("API error: {text}").into()) } } - -// TODO: cover in integration test -pub async fn my_profile(hostname: &str) -> MyResult { - let req = CLIENT.get(format!("http://{}/api/v1/account/my_profile", hostname)); - handle_json_res::(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(()) -} diff --git a/src/frontend/app.rs b/src/frontend/app.rs index c921171..45ea374 100644 --- a/src/frontend/app.rs +++ b/src/frontend/app.rs @@ -1,5 +1,5 @@ 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::pages::article::Article; use crate::frontend::pages::login::Login; @@ -40,11 +40,10 @@ impl GlobalState { } pub fn update_my_profile(&self) { - let backend_hostname_ = self.backend_hostname.clone(); create_local_resource( - move || backend_hostname_.clone(), - |backend_hostname| async move { - if let Ok(my_profile) = my_profile(&backend_hostname).await { + move || (), + |_| async move { + if let Ok(my_profile) = GlobalState::api_client().my_profile().await { expect_context::>() .update(|state| state.my_profile = Some(my_profile.clone())) }; diff --git a/src/frontend/components/nav.rs b/src/frontend/components/nav.rs index c088de5..b12ef71 100644 --- a/src/frontend/components/nav.rs +++ b/src/frontend/components/nav.rs @@ -1,4 +1,3 @@ -use crate::frontend::api::logout; use crate::frontend::app::GlobalState; use leptos::*; use leptos::{component, use_context, view, IntoView, RwSignal, SignalWith}; @@ -46,7 +45,7 @@ fn do_logout() { dbg!("do logout"); create_action(move |()| async move { dbg!("run logout action"); - logout(&GlobalState::read_hostname()).await.unwrap(); + GlobalState::api_client().logout().await.unwrap(); expect_context::>() .get() .update_my_profile(); diff --git a/tests/common.rs b/tests/common.rs index 1e3a8bc..83e17f3 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -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::common::ArticleView; + use ibis_lib::common::RegisterUserData; use ibis_lib::frontend::api::ApiClient; -use ibis_lib::frontend::api::{get_query, handle_json_res}; use ibis_lib::frontend::error::MyResult; use reqwest::cookie::Jar; -use reqwest::{ClientBuilder, StatusCode}; -use serde::de::Deserialize; +use reqwest::ClientBuilder; + use std::env::current_dir; use std::fs::create_dir_all; use std::ops::Deref; @@ -25,7 +18,6 @@ use std::thread::{sleep, spawn}; use std::time::Duration; use tokio::task::JoinHandle; use tracing::log::LevelFilter; -use url::Url; pub struct TestData { pub alpha: IbisInstance, @@ -164,27 +156,5 @@ impl Deref for IbisInstance { &self.api_client } } + pub const TEST_ARTICLE_DEFAULT_TEXT: &str = "some\nexample\ntext\n"; - -pub async fn get_conflicts(instance: &IbisInstance) -> MyResult> { - 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 { - 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()) -} diff --git a/tests/test.rs b/tests/test.rs index 8c6a8c0..b5cdaff 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2,16 +2,11 @@ extern crate ibis_lib; mod common; -use crate::common::fork_article; -use crate::common::get_conflicts; use crate::common::{TestData, TEST_ARTICLE_DEFAULT_TEXT}; -use ibis_lib::backend::api::article::{CreateArticleData, EditArticleData, ForkArticleData}; -use ibis_lib::backend::api::{ResolveObject, SearchArticleData}; -use ibis_lib::backend::database::instance::{DbInstance, InstanceView}; +use ibis_lib::backend::api::article::{EditArticleData, ForkArticleData}; +use ibis_lib::backend::api::SearchArticleData; use ibis_lib::common::{ArticleView, GetArticleData}; -use ibis_lib::common::{DbArticle, LoginUserData, RegisterUserData}; -use ibis_lib::frontend::api::get_query; -use ibis_lib::frontend::api::handle_json_res; +use ibis_lib::common::{LoginUserData, RegisterUserData}; use ibis_lib::frontend::error::MyResult; use pretty_assertions::{assert_eq, assert_ne}; use url::Url; @@ -146,15 +141,10 @@ async fn test_synchronize_articles() -> MyResult<()> { data.alpha.edit_article(&edit_form).await?; // fetch alpha instance on beta, articles are also fetched automatically - let resolve_object = ResolveObject { - id: Url::parse(&format!("http://{}", &data.alpha.hostname))?, - }; - let instance: DbInstance = get_query::( - &data.beta.hostname, - "instance/resolve", - Some(resolve_object), - ) - .await?; + let instance = data + .beta + .resolve_instance(Url::parse(&format!("http://{}", &data.alpha.hostname))?) + .await?; let mut get_article_data = GetArticleData { title: Some(create_res.article.title), @@ -338,7 +328,7 @@ async fn test_local_edit_conflict() -> MyResult<()> { .unwrap(); 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!(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?; 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()); data.stop() @@ -373,15 +363,10 @@ async fn test_federated_edit_conflict() -> MyResult<()> { assert!(create_res.article.local); // fetch article to gamma - let resolve_object = ResolveObject { - id: create_res.article.ap_id.inner().clone(), - }; - let resolve_res: ArticleView = get_query( - &data.gamma.hostname, - "article/resolve", - Some(resolve_object), - ) - .await?; + let resolve_res: ArticleView = data + .gamma + .resolve_article(create_res.article.ap_id.inner().clone()) + .await?; assert_eq!(create_res.article.text, resolve_res.article.text); // alpha edits article @@ -421,7 +406,7 @@ async fn test_federated_edit_conflict() -> MyResult<()> { assert_eq!(1, edit_res.edits.len()); assert!(!edit_res.article.local); - let conflicts = get_conflicts(&data.gamma).await?; + let conflicts = data.gamma.get_conflicts().await?; assert_eq!(1, conflicts.len()); // 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!(3, edit_res.edits.len()); - let conflicts = get_conflicts(&data.gamma).await?; + let conflicts = data.gamma.get_conflicts().await?; assert_eq!(0, conflicts.len()); data.stop() @@ -473,7 +458,7 @@ async fn test_overlapping_edits_no_conflict() -> MyResult<()> { resolve_conflict_id: None, }; 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!(3, edit_res.edits.len()); 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); // fetch on beta - let resolve_object = ResolveObject { - id: create_res.article.ap_id.into_inner(), - }; - let resolve_res: ArticleView = - get_query(&data.beta.hostname, "article/resolve", Some(resolve_object)).await?; + let resolve_res = data + .beta + .resolve_article(create_res.article.ap_id.into_inner()) + .await?; let resolved_article = resolve_res.article; assert_eq!(create_res.edits.len(), resolve_res.edits.len()); @@ -507,7 +491,7 @@ async fn test_fork_article() -> MyResult<()> { let fork_form = ForkArticleData { 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; assert_eq!(resolved_article.title, forked_article.title); 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?; - let title = "Manu_Chao".to_string(); - let create_form = CreateArticleData { - title: title.clone(), - }; + let my_profile = data.alpha.my_profile().await?; + assert_eq!(username, my_profile.person.username); - let req = data - .alpha - .api_client - .client - .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.alpha.logout().await?; + + let my_profile_after_logout = data.alpha.my_profile().await; + assert!(my_profile_after_logout.is_err()); data.stop() }