Skip to content

Commit 6a9e2d5

Browse files
committed
feat(api): [#143] axum api, GET /stats endpoint
1 parent 5ee3f93 commit 6a9e2d5

File tree

3 files changed

+117
-6
lines changed

3 files changed

+117
-6
lines changed

src/apis/routes.rs

+58
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,65 @@
1+
use std::sync::Arc;
2+
3+
use axum::extract::State;
14
use axum::response::Json;
25
use serde_json::{json, Value};
36

7+
use crate::api::resource::stats::Stats;
8+
use crate::tracker::Tracker;
9+
410
#[allow(clippy::unused_async)]
511
pub async fn root() -> Json<Value> {
612
Json(json!({ "data": 42 }))
713
}
14+
15+
#[allow(clippy::unused_async)]
16+
pub async fn get_stats(State(tracker): State<Arc<Tracker>>) -> Json<Value> {
17+
let mut results = Stats {
18+
torrents: 0,
19+
seeders: 0,
20+
completed: 0,
21+
leechers: 0,
22+
tcp4_connections_handled: 0,
23+
tcp4_announces_handled: 0,
24+
tcp4_scrapes_handled: 0,
25+
tcp6_connections_handled: 0,
26+
tcp6_announces_handled: 0,
27+
tcp6_scrapes_handled: 0,
28+
udp4_connections_handled: 0,
29+
udp4_announces_handled: 0,
30+
udp4_scrapes_handled: 0,
31+
udp6_connections_handled: 0,
32+
udp6_announces_handled: 0,
33+
udp6_scrapes_handled: 0,
34+
};
35+
36+
let db = tracker.get_torrents().await;
37+
38+
db.values().for_each(|torrent_entry| {
39+
let (seeders, completed, leechers) = torrent_entry.get_stats();
40+
results.seeders += seeders;
41+
results.completed += completed;
42+
results.leechers += leechers;
43+
results.torrents += 1;
44+
});
45+
46+
let stats = tracker.get_stats().await;
47+
48+
#[allow(clippy::cast_possible_truncation)]
49+
{
50+
results.tcp4_connections_handled = stats.tcp4_connections_handled as u32;
51+
results.tcp4_announces_handled = stats.tcp4_announces_handled as u32;
52+
results.tcp4_scrapes_handled = stats.tcp4_scrapes_handled as u32;
53+
results.tcp6_connections_handled = stats.tcp6_connections_handled as u32;
54+
results.tcp6_announces_handled = stats.tcp6_announces_handled as u32;
55+
results.tcp6_scrapes_handled = stats.tcp6_scrapes_handled as u32;
56+
results.udp4_connections_handled = stats.udp4_connections_handled as u32;
57+
results.udp4_announces_handled = stats.udp4_announces_handled as u32;
58+
results.udp4_scrapes_handled = stats.udp4_scrapes_handled as u32;
59+
results.udp6_connections_handled = stats.udp6_connections_handled as u32;
60+
results.udp6_announces_handled = stats.udp6_announces_handled as u32;
61+
results.udp6_scrapes_handled = stats.udp6_scrapes_handled as u32;
62+
}
63+
64+
Json(json!(results))
65+
}

src/apis/server.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ use axum::Router;
66
use futures::Future;
77
use warp::hyper;
88

9-
use super::routes::root;
9+
use super::routes::{get_stats, root};
1010
use crate::tracker;
1111

12-
pub fn start(socket_addr: SocketAddr, _tracker: &Arc<tracker::Tracker>) -> impl Future<Output = hyper::Result<()>> {
13-
let app = Router::new().route("/", get(root));
12+
pub fn start(socket_addr: SocketAddr, tracker: &Arc<tracker::Tracker>) -> impl Future<Output = hyper::Result<()>> {
13+
let app = Router::new()
14+
.route("/", get(root))
15+
.route("/stats", get(get_stats).with_state(tracker.clone()));
1416

1517
let server = axum::Server::bind(&socket_addr).serve(app.into_make_service());
1618

tests/tracker_api.rs

+54-3
Original file line numberDiff line numberDiff line change
@@ -582,18 +582,69 @@ mod tracker_apis {
582582

583583
mod for_entrypoint {
584584
use crate::api::client::{Client, Query};
585-
use crate::api::server::start_default_api_server;
585+
use crate::api::server::start_default_api;
586586
use crate::api::Version;
587587

588588
#[tokio::test]
589589
async fn test_entrypoint() {
590-
let api_server = start_default_api_server(&Version::Axum).await;
590+
let api_server = start_default_api(&Version::Axum).await;
591591

592592
let response = Client::new(api_server.get_connection_info(), &Version::Axum)
593-
.get("/", Query::default())
593+
.get("", Query::default())
594594
.await;
595595

596596
assert_eq!(response.status(), 200);
597597
}
598598
}
599+
600+
mod for_stats_resources {
601+
use std::str::FromStr;
602+
603+
use torrust_tracker::api::resource::stats::Stats;
604+
use torrust_tracker::protocol::info_hash::InfoHash;
605+
606+
use crate::api::client::Client;
607+
use crate::api::fixtures::sample_peer;
608+
use crate::api::server::start_default_api;
609+
use crate::api::Version;
610+
611+
#[tokio::test]
612+
async fn should_allow_getting_tracker_statistics() {
613+
let api_server = start_default_api(&Version::Axum).await;
614+
615+
api_server
616+
.add_torrent(
617+
&InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(),
618+
&sample_peer(),
619+
)
620+
.await;
621+
622+
let response = Client::new(api_server.get_connection_info(), &Version::Axum)
623+
.get_tracker_statistics()
624+
.await;
625+
626+
assert_eq!(response.status(), 200);
627+
assert_eq!(
628+
response.json::<Stats>().await.unwrap(),
629+
Stats {
630+
torrents: 1,
631+
seeders: 1,
632+
completed: 0,
633+
leechers: 0,
634+
tcp4_connections_handled: 0,
635+
tcp4_announces_handled: 0,
636+
tcp4_scrapes_handled: 0,
637+
tcp6_connections_handled: 0,
638+
tcp6_announces_handled: 0,
639+
tcp6_scrapes_handled: 0,
640+
udp4_connections_handled: 0,
641+
udp4_announces_handled: 0,
642+
udp4_scrapes_handled: 0,
643+
udp6_connections_handled: 0,
644+
udp6_announces_handled: 0,
645+
udp6_scrapes_handled: 0,
646+
}
647+
);
648+
}
649+
}
599650
}

0 commit comments

Comments
 (0)