Skip to content

Commit

Permalink
refactor: [#599] extract types for config::v1::tracker::Tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
josecelano committed May 23, 2024
1 parent b1d5267 commit 7900f12
Show file tree
Hide file tree
Showing 16 changed files with 47 additions and 71 deletions.
27 changes: 4 additions & 23 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,15 +325,11 @@ impl Configuration {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ConfigurationPublic {
website_name: String,
tracker_url: String,
tracker_url: Url,
tracker_mode: TrackerMode,
email_on_signup: EmailOnSignup,
}

fn parse_url(url_str: &str) -> Result<Url, url::ParseError> {
Url::parse(url_str)
}

#[cfg(test)]
mod tests {

Expand Down Expand Up @@ -550,24 +546,9 @@ mod tests {
});
}

mod syntax_checks {
// todo: use rich types in configuration structs for basic syntax checks.

use crate::config::validator::Validator;
use crate::config::Configuration;

#[tokio::test]
async fn tracker_url_should_be_a_valid_url() {
let configuration = Configuration::default();

let mut settings_lock = configuration.settings.write().await;
settings_lock.tracker.url = "INVALID URL".to_string();

assert!(settings_lock.validate().is_err());
}
}

mod semantic_validation {
use url::Url;

use crate::config::validator::Validator;
use crate::config::{Configuration, TrackerMode};

Expand All @@ -577,7 +558,7 @@ mod tests {

let mut settings_lock = configuration.settings.write().await;
settings_lock.tracker.mode = TrackerMode::Private;
settings_lock.tracker.url = "udp://localhost:6969".to_string();
settings_lock.tracker.url = Url::parse("udp://localhost:6969").unwrap();

assert!(settings_lock.validate().is_err());
}
Expand Down
17 changes: 4 additions & 13 deletions src/config/v1/tracker.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use serde::{Deserialize, Serialize};
use torrust_index_located_error::Located;
use url::Url;

use super::{ValidationError, Validator};
use crate::config::{parse_url, TrackerMode};
use crate::config::TrackerMode;

/// Configuration for the associated tracker.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Tracker {
/// Connection string for the tracker. For example: `udp://TRACKER_IP:6969`.
pub url: String,
pub url: Url,
/// The mode of the tracker. For example: `Public`.
/// See `TrackerMode` in [`torrust-tracker-primitives`](https://docs.rs/torrust-tracker-primitives)
/// crate for more information.
Expand All @@ -32,15 +32,6 @@ impl Validator for Tracker {
let tracker_mode = self.mode.clone();
let tracker_url = self.url.clone();

let tracker_url = match parse_url(&tracker_url) {
Ok(url) => url,
Err(err) => {
return Err(ValidationError::InvalidTrackerUrl {
source: Located(err).into(),
})
}
};

if tracker_mode.is_close() && (tracker_url.scheme() != "http" && tracker_url.scheme() != "https") {
return Err(ValidationError::UdpTrackersInPrivateModeNotSupported);
}
Expand All @@ -52,7 +43,7 @@ impl Validator for Tracker {
impl Default for Tracker {
fn default() -> Self {
Self {
url: "udp://localhost:6969".to_string(),
url: Url::parse("udp://localhost:6969").unwrap(),
mode: TrackerMode::default(),
api_url: "http://localhost:1212".to_string(),
token: "MyAccessToken".to_string(),
Expand Down
6 changes: 0 additions & 6 deletions src/config/validator.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
//! Trait to validate the whole settings of sections of the settings.
use thiserror::Error;
use torrust_index_located_error::LocatedError;
use url::ParseError;

/// Errors that can occur validating the configuration.
#[derive(Error, Debug)]
pub enum ValidationError {
/// Unable to load the configuration from the configuration file.
#[error("Invalid tracker URL: {source}")]
InvalidTrackerUrl { source: LocatedError<'static, ParseError> },

#[error("UDP private trackers are not supported. URL schemes for private tracker URLs must be HTTP ot HTTPS")]
UdpTrackersInPrivateModeNotSupported,
}
Expand Down
2 changes: 1 addition & 1 deletion src/console/commands/tracker_statistics_importer/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub async fn import() {

let tracker_url = settings.tracker.url.clone();

eprintln!("Tracker url: {}", tracker_url.green());
eprintln!("Tracker url: {}", tracker_url.to_string().green());

let database = Arc::new(
database::connect(settings.database.connect_url.as_ref())
Expand Down
3 changes: 2 additions & 1 deletion src/databases/database.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use async_trait::async_trait;
use chrono::{DateTime, NaiveDateTime, Utc};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::databases::mysql::Mysql;
use crate::databases::sqlite::Sqlite;
Expand Down Expand Up @@ -336,7 +337,7 @@ pub trait Database: Sync + Send {
async fn get_tags_for_torrent_id(&self, torrent_id: i64) -> Result<Vec<TorrentTag>, Error>;

/// Update the seeders and leechers info for a torrent with `torrent_id`, `tracker_url`, `seeders` and `leechers`.
async fn update_tracker_info(&self, torrent_id: i64, tracker_url: &str, seeders: i64, leechers: i64) -> Result<(), Error>;
async fn update_tracker_info(&self, torrent_id: i64, tracker_url: &Url, seeders: i64, leechers: i64) -> Result<(), Error>;

/// Delete a torrent with `torrent_id`.
async fn delete_torrent(&self, torrent_id: i64) -> Result<(), Error>;
Expand Down
5 changes: 3 additions & 2 deletions src/databases/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use async_trait::async_trait;
use chrono::{DateTime, NaiveDateTime, Utc};
use sqlx::mysql::{MySqlConnectOptions, MySqlPoolOptions};
use sqlx::{query, query_as, Acquire, ConnectOptions, MySqlPool};
use url::Url;

use super::database::TABLES_TO_TRUNCATE;
use crate::databases::database;
Expand Down Expand Up @@ -1072,13 +1073,13 @@ impl Database for Mysql {
async fn update_tracker_info(
&self,
torrent_id: i64,
tracker_url: &str,
tracker_url: &Url,
seeders: i64,
leechers: i64,
) -> Result<(), database::Error> {
query("REPLACE INTO torrust_torrent_tracker_stats (torrent_id, tracker_url, seeders, leechers, updated_at) VALUES (?, ?, ?, ?, ?)")
.bind(torrent_id)
.bind(tracker_url)
.bind(tracker_url.to_string())
.bind(seeders)
.bind(leechers)
.bind(datetime_now())
Expand Down
5 changes: 3 additions & 2 deletions src/databases/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use async_trait::async_trait;
use chrono::{DateTime, NaiveDateTime, Utc};
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use sqlx::{query, query_as, Acquire, ConnectOptions, SqlitePool};
use url::Url;

use super::database::TABLES_TO_TRUNCATE;
use crate::databases::database;
Expand Down Expand Up @@ -1064,13 +1065,13 @@ impl Database for Sqlite {
async fn update_tracker_info(
&self,
torrent_id: i64,
tracker_url: &str,
tracker_url: &Url,
seeders: i64,
leechers: i64,
) -> Result<(), database::Error> {
query("REPLACE INTO torrust_torrent_tracker_stats (torrent_id, tracker_url, seeders, leechers, updated_at) VALUES ($1, $2, $3, $4, $5)")
.bind(torrent_id)
.bind(tracker_url)
.bind(tracker_url.to_string())
.bind(seeders)
.bind(leechers)
.bind(datetime_now())
Expand Down
7 changes: 4 additions & 3 deletions src/models/response.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use url::Url;

use super::category::Category;
use super::torrent::TorrentId;
Expand Down Expand Up @@ -107,12 +108,12 @@ impl TorrentResponse {
}

/// It adds the tracker URL in the first position of the tracker list.
pub fn include_url_as_main_tracker(&mut self, tracker_url: &str) {
pub fn include_url_as_main_tracker(&mut self, tracker_url: &Url) {
// Remove any existing instances of tracker_url
self.trackers.retain(|tracker| tracker != tracker_url);
self.trackers.retain(|tracker| *tracker != tracker_url.to_string());

// Insert tracker_url at the first position
self.trackers.insert(0, tracker_url.to_owned());
self.trackers.insert(0, tracker_url.to_owned().to_string());
}
}

Expand Down
13 changes: 7 additions & 6 deletions src/models/torrent_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
use serde_bencode::ser;
use serde_bytes::ByteBuf;
use sha1::{Digest, Sha1};
use url::Url;

use super::info_hash::InfoHash;
use crate::utils::hex::{from_bytes, into_bytes};
Expand Down Expand Up @@ -127,14 +128,14 @@ impl Torrent {
///
/// It will be the URL in the `announce` field and also the first URL in the
/// `announce_list`.
pub fn include_url_as_main_tracker(&mut self, tracker_url: &str) {
pub fn include_url_as_main_tracker(&mut self, tracker_url: &Url) {
self.set_announce_to(tracker_url);
self.add_url_to_front_of_announce_list(tracker_url);
}

/// Sets the announce url to the tracker url.
pub fn set_announce_to(&mut self, tracker_url: &str) {
self.announce = Some(tracker_url.to_owned());
pub fn set_announce_to(&mut self, tracker_url: &Url) {
self.announce = Some(tracker_url.to_owned().to_string());
}

/// Adds a new tracker URL to the front of the `announce_list`, removes duplicates,
Expand All @@ -146,15 +147,15 @@ impl Torrent {
/// a strict requirement of the `BitTorrent` protocol; it's more of a
/// convention followed by some torrent creators for redundancy and to
/// ensure better availability of trackers.
pub fn add_url_to_front_of_announce_list(&mut self, tracker_url: &str) {
pub fn add_url_to_front_of_announce_list(&mut self, tracker_url: &Url) {
if let Some(list) = &mut self.announce_list {
// Remove the tracker URL from existing lists
for inner_list in list.iter_mut() {
inner_list.retain(|url| url != tracker_url);
inner_list.retain(|url| *url != tracker_url.to_string());
}

// Prepend a new vector containing the tracker_url
let vec = vec![tracker_url.to_owned()];
let vec = vec![tracker_url.to_owned().to_string()];
list.insert(0, vec);

// Remove any empty inner lists
Expand Down
3 changes: 2 additions & 1 deletion src/services/torrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::Arc;

use log::debug;
use serde_derive::{Deserialize, Serialize};
use url::Url;

use super::category::DbCategoryRepository;
use super::user::DbUserRepository;
Expand Down Expand Up @@ -432,7 +433,7 @@ impl Index {
Ok(torrent_response)
}

async fn get_tracker_url(&self) -> String {
async fn get_tracker_url(&self) -> Url {
let settings = self.configuration.settings.read().await;
settings.tracker.url.clone()
}
Expand Down
9 changes: 5 additions & 4 deletions src/tracker/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use derive_more::{Display, Error};
use hyper::StatusCode;
use log::{debug, error};
use serde::{Deserialize, Serialize};
use url::Url;

use super::api::{Client, ConnectionInfo};
use crate::config::Configuration;
Expand Down Expand Up @@ -77,7 +78,7 @@ pub struct Service {
database: Arc<Box<dyn Database>>,
api_client: Client,
token_valid_seconds: u64,
tracker_url: String,
tracker_url: Url,
}

impl Service {
Expand Down Expand Up @@ -197,7 +198,7 @@ impl Service {
///
/// Will return an error if the HTTP request to get generated a new
/// user tracker key failed.
pub async fn get_personal_announce_url(&self, user_id: UserId) -> Result<String, TrackerAPIError> {
pub async fn get_personal_announce_url(&self, user_id: UserId) -> Result<Url, TrackerAPIError> {
debug!(target: "tracker-service", "get personal announce url for user: {user_id}");

let tracker_key = self.database.get_user_tracker_key(user_id).await;
Expand Down Expand Up @@ -366,8 +367,8 @@ impl Service {

/// It builds the announce url appending the user tracker key.
/// Eg: <https://tracker:7070/USER_TRACKER_KEY>
fn announce_url_with_key(&self, tracker_key: &TrackerKey) -> String {
format!("{}/{}", self.tracker_url, tracker_key.key)
fn announce_url_with_key(&self, tracker_key: &TrackerKey) -> Url {
Url::parse(&format!("{}/{}", self.tracker_url, tracker_key.key)).unwrap()
}

fn invalid_token_body() -> String {
Expand Down
7 changes: 4 additions & 3 deletions src/tracker/statistics_importer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::time::Instant;
use chrono::{DateTime, Utc};
use log::{debug, error, info};
use text_colorizer::Colorize;
use url::Url;

use super::service::{Service, TorrentInfo, TrackerAPIError};
use crate::config::Configuration;
Expand All @@ -14,7 +15,7 @@ const LOG_TARGET: &str = "Tracker Stats Importer";
pub struct StatisticsImporter {
database: Arc<Box<dyn Database>>,
tracker_service: Arc<Service>,
tracker_url: String,
tracker_url: Url,
}

impl StatisticsImporter {
Expand All @@ -41,7 +42,7 @@ impl StatisticsImporter {
return Ok(());
}

info!(target: LOG_TARGET, "Importing {} torrents statistics from tracker {} ...", torrents.len().to_string().yellow(), self.tracker_url.yellow());
info!(target: LOG_TARGET, "Importing {} torrents statistics from tracker {} ...", torrents.len().to_string().yellow(), self.tracker_url.to_string().yellow());

// Start the timer before the loop
let start_time = Instant::now();
Expand Down Expand Up @@ -91,7 +92,7 @@ impl StatisticsImporter {
return Ok(());
}

info!(target: LOG_TARGET, "Importing {} torrents statistics from tracker {} ...", torrents.len().to_string().yellow(), self.tracker_url.yellow());
info!(target: LOG_TARGET, "Importing {} torrents statistics from tracker {} ...", torrents.len().to_string().yellow(), self.tracker_url.to_string().yellow());

// Import stats for all torrents in one request

Expand Down
2 changes: 1 addition & 1 deletion src/web/api/client/v1/contexts/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl From<DomainWebsite> for Website {
impl From<DomainTracker> for Tracker {
fn from(tracker: DomainTracker) -> Self {
Self {
url: tracker.url,
url: tracker.url.to_string(),
mode: format!("{:?}", tracker.mode),
api_url: tracker.api_url,
token: tracker.token,
Expand Down
3 changes: 2 additions & 1 deletion tests/common/contexts/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use torrust_index::config::{
Network as DomainNetwork, Settings as DomainSettings, Tracker as DomainTracker,
TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
};
use url::Url;

#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
pub struct Settings {
Expand All @@ -27,7 +28,7 @@ pub struct Website {

#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
pub struct Tracker {
pub url: String,
pub url: Url,
pub mode: String,
pub api_url: String,
pub token: String,
Expand Down
3 changes: 2 additions & 1 deletion tests/common/contexts/settings/responses.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::Deserialize;
use url::Url;

use super::Settings;

Expand All @@ -15,7 +16,7 @@ pub struct PublicSettingsResponse {
#[derive(Deserialize, PartialEq, Debug)]
pub struct Public {
pub website_name: String,
pub tracker_url: String,
pub tracker_url: Url,
pub tracker_mode: String,
pub email_on_signup: String,
}
Expand Down
Loading

0 comments on commit 7900f12

Please sign in to comment.