Skip to content

Commit ca257ff

Browse files
committed
refactor(api): [#182] Axum API, torrent context, update torrent info
1 parent 4bed98a commit ca257ff

File tree

5 files changed

+89
-14
lines changed

5 files changed

+89
-14
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use serde::Deserialize;
2+
3+
use crate::models::torrent_tag::TagId;
4+
5+
#[derive(Debug, Deserialize)]
6+
pub struct UpdateTorrentInfoForm {
7+
pub title: Option<String>,
8+
pub description: Option<String>,
9+
pub tags: Option<Vec<TagId>>,
10+
}

src/web/api/v1/contexts/torrent/handlers.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use std::io::{Cursor, Write};
44
use std::str::FromStr;
55
use std::sync::Arc;
66

7-
use axum::extract::{Multipart, Path, Query, State};
7+
use axum::extract::{self, Multipart, Path, Query, State};
88
use axum::response::{IntoResponse, Response};
99
use axum::Json;
1010
use serde::Deserialize;
1111

12+
use super::forms::UpdateTorrentInfoForm;
1213
use super::responses::{new_torrent_response, torrent_file_response};
1314
use crate::common::AppData;
1415
use crate::errors::ServiceError;
@@ -57,6 +58,11 @@ pub async fn upload_torrent_handler(
5758
#[derive(Deserialize)]
5859
pub struct InfoHashParam(pub String);
5960

61+
/// Returns the torrent as a byte stream `application/x-bittorrent`.
62+
///
63+
/// # Errors
64+
///
65+
/// Returns `ServiceError::BadRequest` if the torrent info-hash is invalid.
6066
#[allow(clippy::unused_async)]
6167
pub async fn download_torrent_handler(
6268
State(app_data): State<Arc<AppData>>,
@@ -80,6 +86,13 @@ pub async fn download_torrent_handler(
8086
torrent_file_response(bytes)
8187
}
8288

89+
/// It returns a list of torrents matching the search criteria.
90+
///
91+
/// Eg: `/torrents?categories=music,other,movie&search=bunny&sort=size_DESC`
92+
///
93+
/// # Errors
94+
///
95+
/// It returns an error if the database query fails.
8396
#[allow(clippy::unused_async)]
8497
pub async fn get_torrents_handler(State(app_data): State<Arc<AppData>>, Query(criteria): Query<ListingRequest>) -> Response {
8598
match app_data.torrent_service.generate_torrent_info_listing(&criteria).await {
@@ -88,6 +101,14 @@ pub async fn get_torrents_handler(State(app_data): State<Arc<AppData>>, Query(cr
88101
}
89102
}
90103

104+
/// Get Torrent from the Index
105+
///
106+
/// # Errors
107+
///
108+
/// This function returns an error if:
109+
///
110+
/// - The info-hash is not valid.
111+
/// - Ot there was a problem getting the torrent info from the database.
91112
#[allow(clippy::unused_async)]
92113
pub async fn get_torrent_info_handler(
93114
State(app_data): State<Arc<AppData>>,
@@ -107,6 +128,45 @@ pub async fn get_torrent_info_handler(
107128
}
108129
}
109130

131+
/// Update a the torrent info
132+
///
133+
/// # Errors
134+
///
135+
/// This function will return an error if unable to:
136+
///
137+
/// * Get the user id from the request.
138+
/// * Get the torrent info-hash from the request.
139+
/// * Update the torrent info.
140+
#[allow(clippy::unused_async)]
141+
pub async fn update_torrent_info_handler(
142+
State(app_data): State<Arc<AppData>>,
143+
Extract(maybe_bearer_token): Extract,
144+
Path(info_hash): Path<InfoHashParam>,
145+
extract::Json(update_torrent_info_form): extract::Json<UpdateTorrentInfoForm>,
146+
) -> Response {
147+
let Ok(info_hash) = InfoHash::from_str(&info_hash.0) else { return ServiceError::BadRequest.into_response() };
148+
149+
let user_id = match app_data.auth.get_user_id_from_bearer_token(&maybe_bearer_token).await {
150+
Ok(user_id) => user_id,
151+
Err(err) => return err.into_response(),
152+
};
153+
154+
match app_data
155+
.torrent_service
156+
.update_torrent_info(
157+
&info_hash,
158+
&update_torrent_info_form.title,
159+
&update_torrent_info_form.description,
160+
&update_torrent_info_form.tags,
161+
&user_id,
162+
)
163+
.await
164+
{
165+
Ok(torrent_response) => Json(OkResponseData { data: torrent_response }).into_response(),
166+
Err(err) => err.into_response(),
167+
}
168+
}
169+
110170
/// If the user is logged in, returns the user's ID. Otherwise, returns `None`.
111171
///
112172
/// # Errors

src/web/api/v1/contexts/torrent/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@
328328
//!
329329
//! Refer to the [`DeletedTorrentResponse`](crate::models::response::DeletedTorrentResponse)
330330
//! struct for more information about the response attributes.
331+
pub mod forms;
331332
pub mod handlers;
332333
pub mod responses;
333334
pub mod routes;

src/web/api/v1/contexts/torrent/routes.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
//! Refer to the [API endpoint documentation](crate::web::api::v1::contexts::torrent).
44
use std::sync::Arc;
55

6-
use axum::routing::{get, post};
6+
use axum::routing::{get, post, put};
77
use axum::Router;
88

9-
use super::handlers::{download_torrent_handler, get_torrent_info_handler, get_torrents_handler, upload_torrent_handler};
9+
use super::handlers::{
10+
download_torrent_handler, get_torrent_info_handler, get_torrents_handler, update_torrent_info_handler, upload_torrent_handler,
11+
};
1012
use crate::common::AppData;
1113

1214
/// Routes for the [`torrent`](crate::web::api::v1::contexts::torrent) API context for single resources.
1315
pub fn router_for_single_resources(app_data: Arc<AppData>) -> Router {
14-
let torrent_info_routes = Router::new().route("/", get(get_torrent_info_handler).with_state(app_data.clone()));
16+
let torrent_info_routes = Router::new()
17+
.route("/", get(get_torrent_info_handler).with_state(app_data.clone()))
18+
.route("/", put(update_torrent_info_handler).with_state(app_data.clone()));
1519

1620
Router::new()
1721
.route("/upload", post(upload_torrent_handler).with_state(app_data.clone()))

tests/e2e/contexts/torrent/contract.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,6 @@ mod with_axum_implementation {
11231123
}
11241124

11251125
mod and_non_admins {
1126-
/*
11271126

11281127
use std::env;
11291128

@@ -1136,6 +1135,8 @@ mod with_axum_implementation {
11361135
use crate::e2e::contexts::user::steps::new_logged_in_user;
11371136
use crate::e2e::environment::TestEnv;
11381137

1138+
/*
1139+
11391140
#[tokio::test]
11401141
async fn it_should_not_allow_non_admins_to_delete_torrents() {
11411142
let mut env = TestEnv::new();
@@ -1161,6 +1162,8 @@ mod with_axum_implementation {
11611162
assert_eq!(response.status, 403);
11621163
}
11631164
1165+
*/
1166+
11641167
#[tokio::test]
11651168
async fn it_should_allow_non_admin_users_to_update_someone_elses_torrents() {
11661169
let mut env = TestEnv::new();
@@ -1199,12 +1202,9 @@ mod with_axum_implementation {
11991202

12001203
assert_eq!(response.status, 403);
12011204
}
1202-
1203-
*/
12041205
}
12051206

12061207
mod and_torrent_owners {
1207-
/*
12081208

12091209
use std::env;
12101210

@@ -1259,25 +1259,25 @@ mod with_axum_implementation {
12591259
assert_eq!(torrent.description, new_description);
12601260
assert!(response.is_json_and_ok());
12611261
}
1262-
1263-
*/
12641262
}
12651263

12661264
mod and_admins {
1267-
/*
12681265

12691266
use std::env;
12701267

12711268
use torrust_index_backend::web::api;
12721269

12731270
use crate::common::client::Client;
12741271
use crate::common::contexts::torrent::forms::UpdateTorrentFrom;
1275-
use crate::common::contexts::torrent::responses::{DeletedTorrentResponse, UpdatedTorrentResponse};
1272+
//use crate::common::contexts::torrent::responses::{DeletedTorrentResponse, UpdatedTorrentResponse};
1273+
use crate::common::contexts::torrent::responses::UpdatedTorrentResponse;
12761274
use crate::e2e::config::ENV_VAR_E2E_EXCLUDE_AXUM_IMPL;
12771275
use crate::e2e::contexts::torrent::steps::upload_random_torrent_to_index;
12781276
use crate::e2e::contexts::user::steps::{new_logged_in_admin, new_logged_in_user};
12791277
use crate::e2e::environment::TestEnv;
12801278

1279+
/*
1280+
12811281
#[tokio::test]
12821282
async fn it_should_allow_admins_to_delete_torrents_searching_by_info_hash() {
12831283
let mut env = TestEnv::new();
@@ -1307,6 +1307,8 @@ mod with_axum_implementation {
13071307
assert!(response.is_json_and_ok());
13081308
}
13091309
1310+
*/
1311+
13101312
#[tokio::test]
13111313
async fn it_should_allow_admins_to_update_someone_elses_torrents() {
13121314
let mut env = TestEnv::new();
@@ -1349,8 +1351,6 @@ mod with_axum_implementation {
13491351
assert_eq!(torrent.description, new_description);
13501352
assert!(response.is_json_and_ok());
13511353
}
1352-
1353-
*/
13541354
}
13551355
}
13561356
}

0 commit comments

Comments
 (0)