diff --git a/.github/workflows/deployment.yaml b/.github/workflows/deployment.yaml index 1e0f59b4..fd4e0fd5 100644 --- a/.github/workflows/deployment.yaml +++ b/.github/workflows/deployment.yaml @@ -58,6 +58,7 @@ jobs: cargo publish -p bittorrent-http-protocol cargo publish -p bittorrent-tracker-client cargo publish -p torrust-tracker + cargo publish -p torrust-tracker-api-client cargo publish -p torrust-tracker-client cargo publish -p torrust-tracker-clock cargo publish -p torrust-tracker-configuration diff --git a/Cargo.lock b/Cargo.lock index 68e32ddb..6e7a1011 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3974,6 +3974,7 @@ dependencies = [ "serde_with", "thiserror 2.0.9", "tokio", + "torrust-tracker-api-client", "torrust-tracker-clock", "torrust-tracker-configuration", "torrust-tracker-contrib-bencode", @@ -3990,6 +3991,18 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "torrust-tracker-api-client" +version = "3.0.0-develop" +dependencies = [ + "hyper", + "reqwest", + "serde", + "thiserror 2.0.9", + "url", + "uuid", +] + [[package]] name = "torrust-tracker-client" version = "3.0.0-develop" diff --git a/Cargo.toml b/Cargo.toml index f1ae96da..0bf3e39e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ rust-version.workspace = true version.workspace = true [lib] -name = "torrust_tracker_lib" +name = "torrust_tracker_lib" [workspace.package] authors = ["Nautilus Cyberneering , Mick van Dijke "] @@ -97,6 +97,7 @@ ignored = ["crossbeam-skiplist", "dashmap", "figment", "parking_lot", "serde_byt [dev-dependencies] local-ip-address = "0" mockall = "0" +torrust-tracker-api-client = { version = "3.0.0-develop", path = "packages/tracker-api-client" } torrust-tracker-test-helpers = { version = "3.0.0-develop", path = "packages/test-helpers" } [workspace] @@ -108,6 +109,7 @@ members = [ "packages/primitives", "packages/test-helpers", "packages/torrent-repository", + "packages/tracker-api-client", "packages/tracker-client", ] diff --git a/packages/tracker-api-client/Cargo.toml b/packages/tracker-api-client/Cargo.toml new file mode 100644 index 00000000..ee45e12f --- /dev/null +++ b/packages/tracker-api-client/Cargo.toml @@ -0,0 +1,23 @@ +[package] +description = "A library to interact with the Torrust Tracker REST API." +keywords = ["bittorrent", "client", "tracker"] +license = "LGPL-3.0" +name = "torrust-tracker-api-client" +readme = "README.md" + +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +publish.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[dependencies] +hyper = "1" +reqwest = { version = "0", features = ["json"] } +serde = { version = "1", features = ["derive"] } +thiserror = "2" +url = { version = "2", features = ["serde"] } +uuid = { version = "1", features = ["v4"] } diff --git a/packages/tracker-api-client/README.md b/packages/tracker-api-client/README.md new file mode 100644 index 00000000..3c10cdb5 --- /dev/null +++ b/packages/tracker-api-client/README.md @@ -0,0 +1,23 @@ +# Torrust Tracker API Client + +A library to interact with the Torrust Tracker REST API. + +## License + +**Copyright (c) 2024 The Torrust Developers.** + +This program is free software: you can redistribute it and/or modify it under the terms of the [GNU Lesser General Public License][LGPL_3_0] as published by the [Free Software Foundation][FSF], version 3. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [GNU Lesser General Public License][LGPL_3_0] for more details. + +You should have received a copy of the *GNU Lesser General Public License* along with this program. If not, see . + +Some files include explicit copyright notices and/or license notices. + +### Legacy Exception + +For prosperity, versions of Torrust BitTorrent Tracker Client that are older than five years are automatically granted the [MIT-0][MIT_0] license in addition to the existing [LGPL-3.0-only][LGPL_3_0] license. + +[LGPL_3_0]: ./LICENSE +[MIT_0]: ./docs/licenses/LICENSE-MIT_0 +[FSF]: https://www.fsf.org/ diff --git a/packages/tracker-api-client/docs/licenses/LICENSE-MIT_0 b/packages/tracker-api-client/docs/licenses/LICENSE-MIT_0 new file mode 100644 index 00000000..fc06cc4f --- /dev/null +++ b/packages/tracker-api-client/docs/licenses/LICENSE-MIT_0 @@ -0,0 +1,14 @@ +MIT No Attribution + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/tracker-api-client/src/common/http.rs b/packages/tracker-api-client/src/common/http.rs new file mode 100644 index 00000000..adbc7dc1 --- /dev/null +++ b/packages/tracker-api-client/src/common/http.rs @@ -0,0 +1,57 @@ +pub type ReqwestQuery = Vec; +pub type ReqwestQueryParam = (String, String); + +/// URL Query component +#[derive(Default, Debug)] +pub struct Query { + params: Vec, +} + +impl Query { + #[must_use] + pub fn empty() -> Self { + Self { params: vec![] } + } + + #[must_use] + pub fn params(params: Vec) -> Self { + Self { params } + } + + pub fn add_param(&mut self, param: QueryParam) { + self.params.push(param); + } +} + +impl From for ReqwestQuery { + fn from(url_search_params: Query) -> Self { + url_search_params + .params + .iter() + .map(|param| ReqwestQueryParam::from((*param).clone())) + .collect() + } +} + +/// URL query param +#[derive(Clone, Debug)] +pub struct QueryParam { + name: String, + value: String, +} + +impl QueryParam { + #[must_use] + pub fn new(name: &str, value: &str) -> Self { + Self { + name: name.to_string(), + value: value.to_string(), + } + } +} + +impl From for ReqwestQueryParam { + fn from(param: QueryParam) -> Self { + (param.name, param.value) + } +} diff --git a/packages/tracker-api-client/src/common/mod.rs b/packages/tracker-api-client/src/common/mod.rs new file mode 100644 index 00000000..3883215f --- /dev/null +++ b/packages/tracker-api-client/src/common/mod.rs @@ -0,0 +1 @@ +pub mod http; diff --git a/packages/tracker-api-client/src/connection_info.rs b/packages/tracker-api-client/src/connection_info.rs new file mode 100644 index 00000000..1224527a --- /dev/null +++ b/packages/tracker-api-client/src/connection_info.rs @@ -0,0 +1,154 @@ +use std::str::FromStr; + +use thiserror::Error; +use url::Url; + +#[derive(Clone)] +pub struct ConnectionInfo { + pub origin: Origin, + pub api_token: Option, +} + +impl ConnectionInfo { + #[must_use] + pub fn authenticated(origin: Origin, api_token: &str) -> Self { + Self { + origin, + api_token: Some(api_token.to_string()), + } + } + + #[must_use] + pub fn anonymous(origin: Origin) -> Self { + Self { origin, api_token: None } + } +} + +/// Represents the origin of a HTTP request. +/// +/// The format of the origin is a URL, but only the scheme, host, and port are used. +/// +/// Pattern: `scheme://host:port/` +#[derive(Debug, Clone)] +pub struct Origin { + url: Url, +} + +#[derive(Debug, Error)] +pub enum OriginError { + #[error("Invalid URL: {0}")] + InvalidUrl(#[from] url::ParseError), + + #[error("URL is missing scheme or host")] + InvalidOrigin, + + #[error("Invalid URL scheme, only http and https are supported")] + InvalidScheme, +} + +impl FromStr for Origin { + type Err = OriginError; + + fn from_str(s: &str) -> Result { + let mut url = Url::parse(s).map_err(OriginError::InvalidUrl)?; + + // Ensure the URL has a scheme and host + if url.scheme().is_empty() || url.host().is_none() { + return Err(OriginError::InvalidOrigin); + } + + if url.scheme() != "http" && url.scheme() != "https" { + return Err(OriginError::InvalidScheme); + } + + // Retain only the origin components + url.set_path("/"); + url.set_query(None); + url.set_fragment(None); + + Ok(Origin { url }) + } +} + +impl std::fmt::Display for Origin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.url) + } +} + +impl Origin { + /// # Errors + /// + /// Will return an error if the string is not a valid URL containing a + /// scheme and host. + pub fn new(s: &str) -> Result { + s.parse() + } + + #[must_use] + pub fn url(&self) -> &Url { + &self.url + } +} + +#[cfg(test)] +mod tests { + mod origin { + use crate::connection_info::Origin; + + #[test] + fn should_be_parsed_from_a_string_representing_a_url() { + let origin = Origin::new("https://example.com:8080/path?query#fragment").unwrap(); + + assert_eq!(origin.to_string(), "https://example.com:8080/"); + } + + mod when_parsing_from_url_string { + use crate::connection_info::Origin; + + #[test] + fn should_ignore_default_ports() { + let origin = Origin::new("http://example.com:80").unwrap(); // DevSkim: ignore DS137138 + assert_eq!(origin.to_string(), "http://example.com/"); // DevSkim: ignore DS137138 + + let origin = Origin::new("https://example.com:443").unwrap(); + assert_eq!(origin.to_string(), "https://example.com/"); + } + + #[test] + fn should_add_the_slash_after_the_host() { + let origin = Origin::new("https://example.com:1212").unwrap(); + + assert_eq!(origin.to_string(), "https://example.com:1212/"); + } + + #[test] + fn should_remove_extra_path_and_query_parameters() { + let origin = Origin::new("https://example.com:1212/path/to/resource?query=1#fragment").unwrap(); + + assert_eq!(origin.to_string(), "https://example.com:1212/"); + } + + #[test] + fn should_fail_when_the_scheme_is_missing() { + let result = Origin::new("example.com"); + + assert!(result.is_err()); + } + + #[test] + fn should_fail_when_the_scheme_is_not_supported() { + let result = Origin::new("udp://example.com"); + + assert!(result.is_err()); + } + + #[test] + fn should_fail_when_the_host_is_missing() { + let result = Origin::new("http://"); + + assert!(result.is_err()); + } + } + } +} diff --git a/packages/tracker-api-client/src/lib.rs b/packages/tracker-api-client/src/lib.rs new file mode 100644 index 00000000..baf80e3c --- /dev/null +++ b/packages/tracker-api-client/src/lib.rs @@ -0,0 +1,3 @@ +pub mod common; +pub mod connection_info; +pub mod v1; diff --git a/tests/servers/api/v1/client.rs b/packages/tracker-api-client/src/v1/client.rs similarity index 85% rename from tests/servers/api/v1/client.rs rename to packages/tracker-api-client/src/v1/client.rs index 63533107..d48d4c00 100644 --- a/tests/servers/api/v1/client.rs +++ b/packages/tracker-api-client/src/v1/client.rs @@ -1,10 +1,11 @@ use hyper::HeaderMap; use reqwest::Response; use serde::Serialize; +use url::Url; use uuid::Uuid; use crate::common::http::{Query, QueryParam, ReqwestQuery}; -use crate::servers::api::connection_info::ConnectionInfo; +use crate::connection_info::ConnectionInfo; /// API Client pub struct Client { @@ -13,10 +14,11 @@ pub struct Client { } impl Client { + #[must_use] pub fn new(connection_info: ConnectionInfo) -> Self { Self { connection_info, - base_path: "/api/v1/".to_string(), + base_path: "api/v1/".to_string(), } } @@ -70,6 +72,9 @@ impl Client { self.get_request_with_query(path, query, headers).await } + /// # Panics + /// + /// Will panic if the request can't be sent pub async fn post_empty(&self, path: &str, headers: Option) -> Response { let builder = reqwest::Client::new() .post(self.base_url(path).clone()) @@ -83,6 +88,9 @@ impl Client { builder.send().await.unwrap() } + /// # Panics + /// + /// Will panic if the request can't be sent pub async fn post_form(&self, path: &str, form: &T, headers: Option) -> Response { let builder = reqwest::Client::new() .post(self.base_url(path).clone()) @@ -97,6 +105,9 @@ impl Client { builder.send().await.unwrap() } + /// # Panics + /// + /// Will panic if the request can't be sent async fn delete(&self, path: &str, headers: Option) -> Response { let builder = reqwest::Client::new() .delete(self.base_url(path).clone()) @@ -111,11 +122,11 @@ impl Client { } pub async fn get_request_with_query(&self, path: &str, params: Query, headers: Option) -> Response { - get(&self.base_url(path), Some(params), headers).await + get(self.base_url(path), Some(params), headers).await } pub async fn get_request(&self, path: &str) -> Response { - get(&self.base_url(path), None, None).await + get(self.base_url(path), None, None).await } fn query_with_token(&self) -> Query { @@ -125,12 +136,15 @@ impl Client { } } - fn base_url(&self, path: &str) -> String { - format!("http://{}{}{path}", &self.connection_info.bind_address, &self.base_path) + fn base_url(&self, path: &str) -> Url { + Url::parse(&format!("{}{}{path}", &self.connection_info.origin, &self.base_path)).unwrap() } } -pub async fn get(path: &str, query: Option, headers: Option) -> Response { +/// # Panics +/// +/// Will panic if the request can't be sent +pub async fn get(path: Url, query: Option, headers: Option) -> Response { let builder = reqwest::Client::builder().build().unwrap(); let builder = match query { @@ -147,6 +161,11 @@ pub async fn get(path: &str, query: Option, headers: Option) - } /// Returns a `HeaderMap` with a request id header +/// +/// # Panics +/// +/// Will panic if the request ID can't be parsed into a string. +#[must_use] pub fn headers_with_request_id(request_id: Uuid) -> HeaderMap { let mut headers = HeaderMap::new(); headers.insert("x-request-id", request_id.to_string().parse().unwrap()); diff --git a/packages/tracker-api-client/src/v1/mod.rs b/packages/tracker-api-client/src/v1/mod.rs new file mode 100644 index 00000000..b9babe5b --- /dev/null +++ b/packages/tracker-api-client/src/v1/mod.rs @@ -0,0 +1 @@ +pub mod client; diff --git a/tests/servers/api/connection_info.rs b/tests/servers/api/connection_info.rs index 35314a2f..e78f4cbb 100644 --- a/tests/servers/api/connection_info.rs +++ b/tests/servers/api/connection_info.rs @@ -1,29 +1,9 @@ -pub fn connection_with_invalid_token(bind_address: &str) -> ConnectionInfo { - ConnectionInfo::authenticated(bind_address, "invalid token") -} - -pub fn connection_with_no_token(bind_address: &str) -> ConnectionInfo { - ConnectionInfo::anonymous(bind_address) -} +use torrust_tracker_api_client::connection_info::{ConnectionInfo, Origin}; -#[derive(Clone)] -pub struct ConnectionInfo { - pub bind_address: String, - pub api_token: Option, +pub fn connection_with_invalid_token(origin: Origin) -> ConnectionInfo { + ConnectionInfo::authenticated(origin, "invalid token") } -impl ConnectionInfo { - pub fn authenticated(bind_address: &str, api_token: &str) -> Self { - Self { - bind_address: bind_address.to_string(), - api_token: Some(api_token.to_string()), - } - } - - pub fn anonymous(bind_address: &str) -> Self { - Self { - bind_address: bind_address.to_string(), - api_token: None, - } - } +pub fn connection_with_no_token(origin: Origin) -> ConnectionInfo { + ConnectionInfo::anonymous(origin) } diff --git a/tests/servers/api/environment.rs b/tests/servers/api/environment.rs index 00fb9d05..70f2d4c6 100644 --- a/tests/servers/api/environment.rs +++ b/tests/servers/api/environment.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use bittorrent_primitives::info_hash::InfoHash; use futures::executor::block_on; use tokio::sync::RwLock; +use torrust_tracker_api_client::connection_info::{ConnectionInfo, Origin}; use torrust_tracker_configuration::{Configuration, HttpApi}; use torrust_tracker_lib::bootstrap::app::initialize_with_configuration; use torrust_tracker_lib::bootstrap::jobs::make_rust_tls; @@ -14,8 +15,6 @@ use torrust_tracker_lib::servers::udp::server::banning::BanService; use torrust_tracker_lib::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP; use torrust_tracker_primitives::peer; -use super::connection_info::ConnectionInfo; - pub struct Environment where S: std::fmt::Debug + std::fmt::Display, @@ -93,8 +92,10 @@ impl Environment { } pub fn get_connection_info(&self) -> ConnectionInfo { + let origin = Origin::new(&format!("http://{}/", self.server.state.local_addr)).unwrap(); // DevSkim: ignore DS137138 + ConnectionInfo { - bind_address: self.server.state.local_addr.to_string(), + origin, api_token: self.config.access_tokens.get("admin").cloned(), } } diff --git a/tests/servers/api/v1/contract/authentication.rs b/tests/servers/api/v1/contract/authentication.rs index 4e0cf49d..6cb1e52b 100644 --- a/tests/servers/api/v1/contract/authentication.rs +++ b/tests/servers/api/v1/contract/authentication.rs @@ -1,10 +1,10 @@ +use torrust_tracker_api_client::common::http::{Query, QueryParam}; +use torrust_tracker_api_client::v1::client::{headers_with_request_id, Client}; use torrust_tracker_test_helpers::configuration; use uuid::Uuid; -use crate::common::http::{Query, QueryParam}; use crate::common::logging::{self, logs_contains_a_line_with}; use crate::servers::api::v1::asserts::{assert_token_not_valid, assert_unauthorized}; -use crate::servers::api::v1::client::{headers_with_request_id, Client}; use crate::servers::api::Started; #[tokio::test] diff --git a/tests/servers/api/v1/contract/context/auth_key.rs b/tests/servers/api/v1/contract/context/auth_key.rs index 4dc039a9..9b2e740c 100644 --- a/tests/servers/api/v1/contract/context/auth_key.rs +++ b/tests/servers/api/v1/contract/context/auth_key.rs @@ -1,6 +1,7 @@ use std::time::Duration; use serde::Serialize; +use torrust_tracker_api_client::v1::client::{headers_with_request_id, AddKeyForm, Client}; use torrust_tracker_lib::core::auth::Key; use torrust_tracker_test_helpers::configuration; use uuid::Uuid; @@ -12,7 +13,6 @@ use crate::servers::api::v1::asserts::{ assert_invalid_auth_key_get_param, assert_invalid_auth_key_post_param, assert_ok, assert_token_not_valid, assert_unauthorized, assert_unprocessable_auth_key_duration_param, }; -use crate::servers::api::v1::client::{headers_with_request_id, AddKeyForm, Client}; use crate::servers::api::{force_database_error, Started}; #[tokio::test] @@ -81,7 +81,7 @@ async fn should_not_allow_generating_a_new_auth_key_for_unauthenticated_users() let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .add_auth_key( AddKeyForm { opt_key: None, @@ -100,7 +100,7 @@ async fn should_not_allow_generating_a_new_auth_key_for_unauthenticated_users() let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .add_auth_key( AddKeyForm { opt_key: None, @@ -332,7 +332,7 @@ async fn should_not_allow_deleting_an_auth_key_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .delete_auth_key(&auth_key.key.to_string(), Some(headers_with_request_id(request_id))) .await; @@ -352,7 +352,7 @@ async fn should_not_allow_deleting_an_auth_key_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .delete_auth_key(&auth_key.key.to_string(), Some(headers_with_request_id(request_id))) .await; @@ -433,7 +433,7 @@ async fn should_not_allow_reloading_keys_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .reload_keys(Some(headers_with_request_id(request_id))) .await; @@ -446,7 +446,7 @@ async fn should_not_allow_reloading_keys_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .reload_keys(Some(headers_with_request_id(request_id))) .await; @@ -462,6 +462,7 @@ async fn should_not_allow_reloading_keys_for_unauthenticated_users() { mod deprecated_generate_key_endpoint { + use torrust_tracker_api_client::v1::client::{headers_with_request_id, Client}; use torrust_tracker_lib::core::auth::Key; use torrust_tracker_test_helpers::configuration; use uuid::Uuid; @@ -472,7 +473,6 @@ mod deprecated_generate_key_endpoint { assert_auth_key_utf8, assert_failed_to_generate_key, assert_invalid_key_duration_param, assert_token_not_valid, assert_unauthorized, }; - use crate::servers::api::v1::client::{headers_with_request_id, Client}; use crate::servers::api::{force_database_error, Started}; #[tokio::test] @@ -507,13 +507,13 @@ mod deprecated_generate_key_endpoint { let request_id = Uuid::new_v4(); let seconds_valid = 60; - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .generate_auth_key(seconds_valid, Some(headers_with_request_id(request_id))) .await; assert_token_not_valid(response).await; - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .generate_auth_key(seconds_valid, None) .await; diff --git a/tests/servers/api/v1/contract/context/health_check.rs b/tests/servers/api/v1/contract/context/health_check.rs index 32228575..4d37917f 100644 --- a/tests/servers/api/v1/contract/context/health_check.rs +++ b/tests/servers/api/v1/contract/context/health_check.rs @@ -1,8 +1,9 @@ +use torrust_tracker_api_client::v1::client::get; use torrust_tracker_lib::servers::apis::v1::context::health_check::resources::{Report, Status}; use torrust_tracker_test_helpers::configuration; +use url::Url; use crate::common::logging; -use crate::servers::api::v1::client::get; use crate::servers::api::Started; #[tokio::test] @@ -11,9 +12,9 @@ async fn health_check_endpoint_should_return_status_ok_if_api_is_running() { let env = Started::new(&configuration::ephemeral().into()).await; - let url = format!("http://{}/api/health_check", env.get_connection_info().bind_address); + let url = Url::parse(&format!("{}api/health_check", env.get_connection_info().origin)).unwrap(); - let response = get(&url, None, None).await; + let response = get(url, None, None).await; assert_eq!(response.status(), 200); assert_eq!(response.headers().get("content-type").unwrap(), "application/json"); diff --git a/tests/servers/api/v1/contract/context/stats.rs b/tests/servers/api/v1/contract/context/stats.rs index bc6e495a..2eda0ed4 100644 --- a/tests/servers/api/v1/contract/context/stats.rs +++ b/tests/servers/api/v1/contract/context/stats.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use bittorrent_primitives::info_hash::InfoHash; +use torrust_tracker_api_client::v1::client::{headers_with_request_id, Client}; use torrust_tracker_lib::servers::apis::v1::context::stats::resources::Stats; use torrust_tracker_primitives::peer::fixture::PeerBuilder; use torrust_tracker_test_helpers::configuration; @@ -9,7 +10,6 @@ use uuid::Uuid; use crate::common::logging::{self, logs_contains_a_line_with}; use crate::servers::api::connection_info::{connection_with_invalid_token, connection_with_no_token}; use crate::servers::api::v1::asserts::{assert_stats, assert_token_not_valid, assert_unauthorized}; -use crate::servers::api::v1::client::{headers_with_request_id, Client}; use crate::servers::api::Started; #[tokio::test] @@ -79,7 +79,7 @@ async fn should_not_allow_getting_tracker_statistics_for_unauthenticated_users() let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .get_tracker_statistics(Some(headers_with_request_id(request_id))) .await; @@ -92,7 +92,7 @@ async fn should_not_allow_getting_tracker_statistics_for_unauthenticated_users() let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .get_tracker_statistics(Some(headers_with_request_id(request_id))) .await; diff --git a/tests/servers/api/v1/contract/context/torrent.rs b/tests/servers/api/v1/contract/context/torrent.rs index 260fe4a3..76646db1 100644 --- a/tests/servers/api/v1/contract/context/torrent.rs +++ b/tests/servers/api/v1/contract/context/torrent.rs @@ -1,20 +1,20 @@ use std::str::FromStr; use bittorrent_primitives::info_hash::InfoHash; +use torrust_tracker_api_client::common::http::{Query, QueryParam}; +use torrust_tracker_api_client::v1::client::{headers_with_request_id, Client}; use torrust_tracker_lib::servers::apis::v1::context::torrent::resources::peer::Peer; use torrust_tracker_lib::servers::apis::v1::context::torrent::resources::torrent::{self, Torrent}; use torrust_tracker_primitives::peer::fixture::PeerBuilder; use torrust_tracker_test_helpers::configuration; use uuid::Uuid; -use crate::common::http::{Query, QueryParam}; use crate::common::logging::{self, logs_contains_a_line_with}; use crate::servers::api::connection_info::{connection_with_invalid_token, connection_with_no_token}; use crate::servers::api::v1::asserts::{ assert_bad_request, assert_invalid_infohash_param, assert_not_found, assert_token_not_valid, assert_torrent_info, assert_torrent_list, assert_torrent_not_known, assert_unauthorized, }; -use crate::servers::api::v1::client::{headers_with_request_id, Client}; use crate::servers::api::v1::contract::fixtures::{ invalid_infohashes_returning_bad_request, invalid_infohashes_returning_not_found, }; @@ -263,7 +263,7 @@ async fn should_not_allow_getting_torrents_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .get_torrents(Query::empty(), Some(headers_with_request_id(request_id))) .await; @@ -276,7 +276,7 @@ async fn should_not_allow_getting_torrents_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .get_torrents(Query::default(), Some(headers_with_request_id(request_id))) .await; @@ -382,7 +382,7 @@ async fn should_not_allow_getting_a_torrent_info_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .get_torrent(&info_hash.to_string(), Some(headers_with_request_id(request_id))) .await; @@ -395,7 +395,7 @@ async fn should_not_allow_getting_a_torrent_info_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .get_torrent(&info_hash.to_string(), Some(headers_with_request_id(request_id))) .await; diff --git a/tests/servers/api/v1/contract/context/whitelist.rs b/tests/servers/api/v1/contract/context/whitelist.rs index d0a80e96..6dde663a 100644 --- a/tests/servers/api/v1/contract/context/whitelist.rs +++ b/tests/servers/api/v1/contract/context/whitelist.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use bittorrent_primitives::info_hash::InfoHash; +use torrust_tracker_api_client::v1::client::{headers_with_request_id, Client}; use torrust_tracker_test_helpers::configuration; use uuid::Uuid; @@ -10,7 +11,6 @@ use crate::servers::api::v1::asserts::{ assert_failed_to_reload_whitelist, assert_failed_to_remove_torrent_from_whitelist, assert_failed_to_whitelist_torrent, assert_invalid_infohash_param, assert_not_found, assert_ok, assert_token_not_valid, assert_unauthorized, }; -use crate::servers::api::v1::client::{headers_with_request_id, Client}; use crate::servers::api::v1::contract::fixtures::{ invalid_infohashes_returning_bad_request, invalid_infohashes_returning_not_found, }; @@ -76,7 +76,7 @@ async fn should_not_allow_whitelisting_a_torrent_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .whitelist_a_torrent(&info_hash, Some(headers_with_request_id(request_id))) .await; @@ -89,7 +89,7 @@ async fn should_not_allow_whitelisting_a_torrent_for_unauthenticated_users() { let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .whitelist_a_torrent(&info_hash, Some(headers_with_request_id(request_id))) .await; @@ -270,7 +270,7 @@ async fn should_not_allow_removing_a_torrent_from_the_whitelist_for_unauthentica let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_invalid_token(env.get_connection_info().origin)) .remove_torrent_from_whitelist(&hash, Some(headers_with_request_id(request_id))) .await; @@ -285,7 +285,7 @@ async fn should_not_allow_removing_a_torrent_from_the_whitelist_for_unauthentica let request_id = Uuid::new_v4(); - let response = Client::new(connection_with_no_token(env.get_connection_info().bind_address.as_str())) + let response = Client::new(connection_with_no_token(env.get_connection_info().origin)) .remove_torrent_from_whitelist(&hash, Some(headers_with_request_id(request_id))) .await; diff --git a/tests/servers/api/v1/mod.rs b/tests/servers/api/v1/mod.rs index 37298b37..e2db6b4c 100644 --- a/tests/servers/api/v1/mod.rs +++ b/tests/servers/api/v1/mod.rs @@ -1,3 +1,2 @@ pub mod asserts; -pub mod client; pub mod contract;