-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
b53ce8d refactor(api): [#198] Axum API, tag context (Jose Celano) Pull request description: API migration to Axum for `tag` context. Top commit has no ACKs. Tree-SHA512: 8d14fa225fa87d54d3aba217a942510113864aff5e98f08e391f50aba304ff79ff65482fb2751e62f2e4bb9153367512dc859948bb2467b03e3999ba0e366fef
- Loading branch information
Showing
11 changed files
with
544 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//! API forms for the the [`tag`](crate::web::api::v1::contexts::tag) API | ||
//! context. | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::models::torrent_tag::TagId; | ||
|
||
#[derive(Serialize, Deserialize, Debug)] | ||
pub struct AddTagForm { | ||
pub name: String, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Debug)] | ||
pub struct DeleteTagForm { | ||
pub tag_id: TagId, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
//! API handlers for the [`tag`](crate::web::api::v1::contexts::tag) API | ||
//! context. | ||
use std::sync::Arc; | ||
|
||
use axum::extract::{self, State}; | ||
use axum::response::Json; | ||
|
||
use super::forms::{AddTagForm, DeleteTagForm}; | ||
use super::responses::{added_tag, deleted_tag}; | ||
use crate::common::AppData; | ||
use crate::databases::database; | ||
use crate::errors::ServiceError; | ||
use crate::models::torrent_tag::TorrentTag; | ||
use crate::web::api::v1::extractors::bearer_token::Extract; | ||
use crate::web::api::v1::responses::{self, OkResponse}; | ||
|
||
/// It handles the request to get all the tags. | ||
/// | ||
/// It returns: | ||
/// | ||
/// - `200` response with a json containing the tag list [`Vec<TorrentTag>`](crate::models::torrent_tag::TorrentTag). | ||
/// - Other error status codes if there is a database error. | ||
/// | ||
/// Refer to the [API endpoint documentation](crate::web::api::v1::contexts::tag) | ||
/// for more information about this endpoint. | ||
/// | ||
/// # Errors | ||
/// | ||
/// It returns an error if there is a database error. | ||
#[allow(clippy::unused_async)] | ||
pub async fn get_all_handler( | ||
State(app_data): State<Arc<AppData>>, | ||
) -> Result<Json<responses::OkResponse<Vec<TorrentTag>>>, database::Error> { | ||
match app_data.tag_repository.get_all().await { | ||
Ok(tags) => Ok(Json(responses::OkResponse { data: tags })), | ||
Err(error) => Err(error), | ||
} | ||
} | ||
|
||
/// It adds a new tag. | ||
/// | ||
/// # Errors | ||
/// | ||
/// It returns an error if: | ||
/// | ||
/// - The user does not have permissions to create a new tag. | ||
/// - There is a database error. | ||
#[allow(clippy::unused_async)] | ||
pub async fn add_handler( | ||
State(app_data): State<Arc<AppData>>, | ||
Extract(maybe_bearer_token): Extract, | ||
extract::Json(add_tag_form): extract::Json<AddTagForm>, | ||
) -> Result<Json<OkResponse<String>>, ServiceError> { | ||
let user_id = app_data.auth.get_user_id_from_bearer_token(&maybe_bearer_token).await?; | ||
|
||
match app_data.tag_service.add_tag(&add_tag_form.name, &user_id).await { | ||
Ok(_) => Ok(added_tag(&add_tag_form.name)), | ||
Err(error) => Err(error), | ||
} | ||
} | ||
|
||
/// It deletes a tag. | ||
/// | ||
/// # Errors | ||
/// | ||
/// It returns an error if: | ||
/// | ||
/// - The user does not have permissions to delete tags. | ||
/// - There is a database error. | ||
#[allow(clippy::unused_async)] | ||
pub async fn delete_handler( | ||
State(app_data): State<Arc<AppData>>, | ||
Extract(maybe_bearer_token): Extract, | ||
extract::Json(delete_tag_form): extract::Json<DeleteTagForm>, | ||
) -> Result<Json<OkResponse<String>>, ServiceError> { | ||
let user_id = app_data.auth.get_user_id_from_bearer_token(&maybe_bearer_token).await?; | ||
|
||
match app_data.tag_service.delete_tag(&delete_tag_form.tag_id, &user_id).await { | ||
Ok(_) => Ok(deleted_tag(delete_tag_form.tag_id)), | ||
Err(error) => Err(error), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
//! API context: `tag`. | ||
//! | ||
//! This API context is responsible for handling torrent tags. | ||
//! | ||
//! # Endpoints | ||
//! | ||
//! - [Get all tags](#get-all-tags) | ||
//! - [Add a tag](#add-a-tag) | ||
//! - [Delete a tag](#delete-a-tag) | ||
//! | ||
//! **NOTICE**: We don't support multiple languages yet, so the tag is always | ||
//! in English. | ||
//! | ||
//! # Get all tags | ||
//! | ||
//! `GET /v1/tag` | ||
//! | ||
//! Returns all torrent tags. | ||
//! | ||
//! **Example request** | ||
//! | ||
//! ```bash | ||
//! curl "http://127.0.0.1:3000/v1/tags" | ||
//! ``` | ||
//! | ||
//! **Example response** `200` | ||
//! | ||
//! ```json | ||
//! { | ||
//! "data": [ | ||
//! { | ||
//! "tag_id": 1, | ||
//! "name": "anime" | ||
//! }, | ||
//! { | ||
//! "tag_id": 2, | ||
//! "name": "manga" | ||
//! } | ||
//! ] | ||
//! } | ||
//! ``` | ||
//! **Resource** | ||
//! | ||
//! Refer to the [`Tag`](crate::databases::database::Tag) | ||
//! struct for more information about the response attributes. | ||
//! | ||
//! # Add a tag | ||
//! | ||
//! `POST /v1/tag` | ||
//! | ||
//! It adds a new tag. | ||
//! | ||
//! **POST params** | ||
//! | ||
//! Name | Type | Description | Required | Example | ||
//! ---|---|---|---|--- | ||
//! `name` | `String` | The tag name | Yes | `new tag` | ||
//! | ||
//! **Example request** | ||
//! | ||
//! ```bash | ||
//! curl \ | ||
//! --header "Content-Type: application/json" \ | ||
//! --header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7InVzZXJfaWQiOjEsInVzZXJuYW1lIjoiaW5kZXhhZG1pbiIsImFkbWluaXN0cmF0b3IiOnRydWV9LCJleHAiOjE2ODYyMTU3ODh9.4k8ty27DiWwOk4WVcYEhIrAndhpXMRWnLZ3i_HlJnvI" \ | ||
//! --request POST \ | ||
//! --data '{"name":"new tag"}' \ | ||
//! http://127.0.0.1:3000/v1/tag | ||
//! ``` | ||
//! | ||
//! **Example response** `200` | ||
//! | ||
//! ```json | ||
//! { | ||
//! "data": "new tag" | ||
//! } | ||
//! ``` | ||
//! | ||
//! **Resource** | ||
//! | ||
//! Refer to [`OkResponse`](crate::models::response::OkResponse<T>) for more | ||
//! information about the response attributes. The response contains only the | ||
//! name of the newly created tag. | ||
//! | ||
//! # Delete a tag | ||
//! | ||
//! `DELETE /v1/tag` | ||
//! | ||
//! It deletes a tag. | ||
//! | ||
//! **POST params** | ||
//! | ||
//! Name | Type | Description | Required | Example | ||
//! ---|---|---|---|--- | ||
//! `tag_id` | `i64` | The internal tag ID | Yes | `1` | ||
//! | ||
//! **Example request** | ||
//! | ||
//! ```bash | ||
//! curl \ | ||
//! --header "Content-Type: application/json" \ | ||
//! --header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7InVzZXJfaWQiOjEsInVzZXJuYW1lIjoiaW5kZXhhZG1pbiIsImFkbWluaXN0cmF0b3IiOnRydWV9LCJleHAiOjE2ODYyMTU3ODh9.4k8ty27DiWwOk4WVcYEhIrAndhpXMRWnLZ3i_HlJnvI" \ | ||
//! --request DELETE \ | ||
//! --data '{"tag_id":1}' \ | ||
//! http://127.0.0.1:3000/v1/tag | ||
//! ``` | ||
//! | ||
//! **Example response** `200` | ||
//! | ||
//! ```json | ||
//! { | ||
//! "data": 1 | ||
//! } | ||
//! ``` | ||
//! | ||
//! **Resource** | ||
//! | ||
//! Refer to [`OkResponse`](crate::models::response::OkResponse<T>) for more | ||
//! information about the response attributes. The response contains only the | ||
//! name of the deleted tag. | ||
pub mod forms; | ||
pub mod handlers; | ||
pub mod responses; | ||
pub mod routes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
//! API responses for the [`tag`](crate::web::api::v1::contexts::tag) API | ||
//! context. | ||
use axum::Json; | ||
|
||
use crate::models::torrent_tag::TagId; | ||
use crate::web::api::v1::responses::OkResponse; | ||
|
||
/// Response after successfully creating a new tag. | ||
pub fn added_tag(tag_name: &str) -> Json<OkResponse<String>> { | ||
Json(OkResponse { | ||
data: tag_name.to_string(), | ||
}) | ||
} | ||
|
||
/// Response after successfully deleting a tag. | ||
pub fn deleted_tag(tag_id: TagId) -> Json<OkResponse<String>> { | ||
Json(OkResponse { | ||
data: tag_id.to_string(), | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
//! API routes for the [`tag`](crate::web::api::v1::contexts::tag) API context. | ||
//! | ||
//! Refer to the [API endpoint documentation](crate::web::api::v1::contexts::tag). | ||
use std::sync::Arc; | ||
|
||
use axum::routing::{delete, get, post}; | ||
use axum::Router; | ||
|
||
use super::handlers::{add_handler, delete_handler, get_all_handler}; | ||
use crate::common::AppData; | ||
|
||
// code-review: should we use `tags` also for single resources? | ||
|
||
/// Routes for the [`tag`](crate::web::api::v1::contexts::tag) API context. | ||
pub fn router_for_single_resources(app_data: Arc<AppData>) -> Router { | ||
Router::new() | ||
.route("/", post(add_handler).with_state(app_data.clone())) | ||
.route("/", delete(delete_handler).with_state(app_data)) | ||
} | ||
|
||
/// Routes for the [`tag`](crate::web::api::v1::contexts::tag) API context. | ||
pub fn router_for_multiple_resources(app_data: Arc<AppData>) -> Router { | ||
Router::new().route("/", get(get_all_handler).with_state(app_data)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use torrust_index_backend::models::torrent_tag::TagId; | ||
|
||
use crate::common::asserts::assert_json_ok; | ||
use crate::common::contexts::tag::responses::{AddedTagResponse, DeletedTagResponse}; | ||
use crate::common::responses::TextResponse; | ||
|
||
pub fn assert_added_tag_response(response: &TextResponse, tag_name: &str) { | ||
let added_tag_response: AddedTagResponse = serde_json::from_str(&response.body) | ||
.unwrap_or_else(|_| panic!("response {:#?} should be a AddedTagResponse", response.body)); | ||
|
||
assert_eq!(added_tag_response.data, tag_name); | ||
|
||
assert_json_ok(response); | ||
} | ||
|
||
pub fn assert_deleted_tag_response(response: &TextResponse, tag_id: TagId) { | ||
let deleted_tag_response: DeletedTagResponse = serde_json::from_str(&response.body) | ||
.unwrap_or_else(|_| panic!("response {:#?} should be a DeletedTagResponse", response.body)); | ||
|
||
assert_eq!(deleted_tag_response.data, tag_id); | ||
|
||
assert_json_ok(response); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub mod asserts; | ||
pub mod fixtures; | ||
pub mod forms; | ||
pub mod responses; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.