Skip to content

Commit

Permalink
refactor(api): [torrust#182] Axum API, torrent context, update torren…
Browse files Browse the repository at this point in the history
…t info
  • Loading branch information
josecelano committed Jun 16, 2023
1 parent 4bed98a commit ca257ff
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 14 deletions.
10 changes: 10 additions & 0 deletions src/web/api/v1/contexts/torrent/forms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use serde::Deserialize;

use crate::models::torrent_tag::TagId;

#[derive(Debug, Deserialize)]
pub struct UpdateTorrentInfoForm {
pub title: Option<String>,
pub description: Option<String>,
pub tags: Option<Vec<TagId>>,
}
62 changes: 61 additions & 1 deletion src/web/api/v1/contexts/torrent/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use std::io::{Cursor, Write};
use std::str::FromStr;
use std::sync::Arc;

use axum::extract::{Multipart, Path, Query, State};
use axum::extract::{self, Multipart, Path, Query, State};
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde::Deserialize;

use super::forms::UpdateTorrentInfoForm;
use super::responses::{new_torrent_response, torrent_file_response};
use crate::common::AppData;
use crate::errors::ServiceError;
Expand Down Expand Up @@ -57,6 +58,11 @@ pub async fn upload_torrent_handler(
#[derive(Deserialize)]
pub struct InfoHashParam(pub String);

/// Returns the torrent as a byte stream `application/x-bittorrent`.
///
/// # Errors
///
/// Returns `ServiceError::BadRequest` if the torrent info-hash is invalid.
#[allow(clippy::unused_async)]
pub async fn download_torrent_handler(
State(app_data): State<Arc<AppData>>,
Expand All @@ -80,6 +86,13 @@ pub async fn download_torrent_handler(
torrent_file_response(bytes)
}

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

/// Get Torrent from the Index
///
/// # Errors
///
/// This function returns an error if:
///
/// - The info-hash is not valid.
/// - Ot there was a problem getting the torrent info from the database.
#[allow(clippy::unused_async)]
pub async fn get_torrent_info_handler(
State(app_data): State<Arc<AppData>>,
Expand All @@ -107,6 +128,45 @@ pub async fn get_torrent_info_handler(
}
}

/// Update a the torrent info
///
/// # Errors
///
/// This function will return an error if unable to:
///
/// * Get the user id from the request.
/// * Get the torrent info-hash from the request.
/// * Update the torrent info.
#[allow(clippy::unused_async)]
pub async fn update_torrent_info_handler(
State(app_data): State<Arc<AppData>>,
Extract(maybe_bearer_token): Extract,
Path(info_hash): Path<InfoHashParam>,
extract::Json(update_torrent_info_form): extract::Json<UpdateTorrentInfoForm>,
) -> Response {
let Ok(info_hash) = InfoHash::from_str(&info_hash.0) else { return ServiceError::BadRequest.into_response() };

let user_id = match app_data.auth.get_user_id_from_bearer_token(&maybe_bearer_token).await {
Ok(user_id) => user_id,
Err(err) => return err.into_response(),
};

match app_data
.torrent_service
.update_torrent_info(
&info_hash,
&update_torrent_info_form.title,
&update_torrent_info_form.description,
&update_torrent_info_form.tags,
&user_id,
)
.await
{
Ok(torrent_response) => Json(OkResponseData { data: torrent_response }).into_response(),
Err(err) => err.into_response(),
}
}

/// If the user is logged in, returns the user's ID. Otherwise, returns `None`.
///
/// # Errors
Expand Down
1 change: 1 addition & 0 deletions src/web/api/v1/contexts/torrent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@
//!
//! Refer to the [`DeletedTorrentResponse`](crate::models::response::DeletedTorrentResponse)
//! struct for more information about the response attributes.
pub mod forms;
pub mod handlers;
pub mod responses;
pub mod routes;
10 changes: 7 additions & 3 deletions src/web/api/v1/contexts/torrent/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
//! Refer to the [API endpoint documentation](crate::web::api::v1::contexts::torrent).
use std::sync::Arc;

use axum::routing::{get, post};
use axum::routing::{get, post, put};
use axum::Router;

use super::handlers::{download_torrent_handler, get_torrent_info_handler, get_torrents_handler, upload_torrent_handler};
use super::handlers::{
download_torrent_handler, get_torrent_info_handler, get_torrents_handler, update_torrent_info_handler, upload_torrent_handler,
};
use crate::common::AppData;

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

Router::new()
.route("/upload", post(upload_torrent_handler).with_state(app_data.clone()))
Expand Down
20 changes: 10 additions & 10 deletions tests/e2e/contexts/torrent/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,6 @@ mod with_axum_implementation {
}

mod and_non_admins {
/*

use std::env;

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

/*
#[tokio::test]
async fn it_should_not_allow_non_admins_to_delete_torrents() {
let mut env = TestEnv::new();
Expand All @@ -1161,6 +1162,8 @@ mod with_axum_implementation {
assert_eq!(response.status, 403);
}
*/

#[tokio::test]
async fn it_should_allow_non_admin_users_to_update_someone_elses_torrents() {
let mut env = TestEnv::new();
Expand Down Expand Up @@ -1199,12 +1202,9 @@ mod with_axum_implementation {

assert_eq!(response.status, 403);
}
*/
}

mod and_torrent_owners {
/*

use std::env;

Expand Down Expand Up @@ -1259,25 +1259,25 @@ mod with_axum_implementation {
assert_eq!(torrent.description, new_description);
assert!(response.is_json_and_ok());
}
*/
}

mod and_admins {
/*

use std::env;

use torrust_index_backend::web::api;

use crate::common::client::Client;
use crate::common::contexts::torrent::forms::UpdateTorrentFrom;
use crate::common::contexts::torrent::responses::{DeletedTorrentResponse, UpdatedTorrentResponse};
//use crate::common::contexts::torrent::responses::{DeletedTorrentResponse, UpdatedTorrentResponse};
use crate::common::contexts::torrent::responses::UpdatedTorrentResponse;
use crate::e2e::config::ENV_VAR_E2E_EXCLUDE_AXUM_IMPL;
use crate::e2e::contexts::torrent::steps::upload_random_torrent_to_index;
use crate::e2e::contexts::user::steps::{new_logged_in_admin, new_logged_in_user};
use crate::e2e::environment::TestEnv;

/*
#[tokio::test]
async fn it_should_allow_admins_to_delete_torrents_searching_by_info_hash() {
let mut env = TestEnv::new();
Expand Down Expand Up @@ -1307,6 +1307,8 @@ mod with_axum_implementation {
assert!(response.is_json_and_ok());
}
*/

#[tokio::test]
async fn it_should_allow_admins_to_update_someone_elses_torrents() {
let mut env = TestEnv::new();
Expand Down Expand Up @@ -1349,8 +1351,6 @@ mod with_axum_implementation {
assert_eq!(torrent.description, new_description);
assert!(response.is_json_and_ok());
}
*/
}
}
}

0 comments on commit ca257ff

Please sign in to comment.