Skip to content

Commit

Permalink
Merge #201: Axum API: settings context
Browse files Browse the repository at this point in the history
dc469c4 refactor(api): [#181] Axum API, settings contex (Jose Celano)

Pull request description:

  API migration to Axum for `settings` context.

Top commit has no ACKs.

Tree-SHA512: 0f6522a66b49278d92502363e787fd2da5431bb4bde1541ddb572d033050e52e0e594ba10c9e600afc3bf38c42bca07a20b41a0bf4be1d5bf797018b3b48b9f2
  • Loading branch information
josecelano committed Jun 15, 2023
2 parents 0c26aaa + dc469c4 commit 619cc33
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 2 deletions.
68 changes: 68 additions & 0 deletions src/web/api/v1/contexts/settings/handlers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//! API handlers for the the [`category`](crate::web::api::v1::contexts::category) API
//! context.
use std::sync::Arc;

use axum::extract::{self, State};
use axum::response::Json;

use crate::common::AppData;
use crate::config::{ConfigurationPublic, TorrustBackend};
use crate::errors::ServiceError;
use crate::web::api::v1::extractors::bearer_token::Extract;
use crate::web::api::v1::responses::{self, OkResponse};

/// Get all settings.
///
/// # Errors
///
/// This function will return an error if the user does not have permission to
/// view all the settings.
#[allow(clippy::unused_async)]
pub async fn get_all_handler(
State(app_data): State<Arc<AppData>>,
Extract(maybe_bearer_token): Extract,
) -> Result<Json<OkResponse<TorrustBackend>>, ServiceError> {
let user_id = app_data.auth.get_user_id_from_bearer_token(&maybe_bearer_token).await?;

let all_settings = app_data.settings_service.get_all(&user_id).await?;

Ok(Json(responses::OkResponse { data: all_settings }))
}

/// Get public Settings.
#[allow(clippy::unused_async)]
pub async fn get_public_handler(State(app_data): State<Arc<AppData>>) -> Json<responses::OkResponse<ConfigurationPublic>> {
let public_settings = app_data.settings_service.get_public().await;

Json(responses::OkResponse { data: public_settings })
}

/// Get website name.
#[allow(clippy::unused_async)]
pub async fn get_site_name_handler(State(app_data): State<Arc<AppData>>) -> Json<responses::OkResponse<String>> {
let site_name = app_data.settings_service.get_site_name().await;

Json(responses::OkResponse { data: site_name })
}

/// Update all the settings.
///
/// # Errors
///
/// This function will return an error if:
///
/// - The user does not have permission to update the settings.
/// - The settings could not be updated because they were loaded from env vars.
/// See <https://github.com/torrust/torrust-index-backend/issues/144.>
#[allow(clippy::unused_async)]
pub async fn update_handler(
State(app_data): State<Arc<AppData>>,
Extract(maybe_bearer_token): Extract,
extract::Json(torrust_backend): extract::Json<TorrustBackend>,
) -> Result<Json<OkResponse<TorrustBackend>>, ServiceError> {
let user_id = app_data.auth.get_user_id_from_bearer_token(&maybe_bearer_token).await?;

let new_settings = app_data.settings_service.update_all(torrust_backend, &user_id).await?;

Ok(Json(responses::OkResponse { data: new_settings }))
}
2 changes: 2 additions & 0 deletions src/web/api/v1/contexts/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,5 @@
//!
//! Refer to the [`ConfigurationPublic`](crate::config::ConfigurationPublic)
//! struct for more information about the response attributes.
pub mod handlers;
pub mod routes;
19 changes: 19 additions & 0 deletions src/web/api/v1/contexts/settings/routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! API routes for the [`settings`](crate::web::api::v1::contexts::settings) API context.
//!
//! Refer to the [API endpoint documentation](crate::web::api::v1::contexts::settings).
use std::sync::Arc;

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

use super::handlers::{get_all_handler, get_public_handler, get_site_name_handler, update_handler};
use crate::common::AppData;

/// Routes for the [`category`](crate::web::api::v1::contexts::category) API context.
pub fn router(app_data: Arc<AppData>) -> Router {
Router::new()
.route("/", get(get_all_handler).with_state(app_data.clone()))
.route("/name", get(get_site_name_handler).with_state(app_data.clone()))
.route("/public", get(get_public_handler).with_state(app_data.clone()))
.route("/", post(update_handler).with_state(app_data))
}
5 changes: 3 additions & 2 deletions src/web/api/v1/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use axum::Router;

use super::contexts::about::handlers::about_page_handler;
//use tower_http::cors::CorsLayer;
use super::contexts::{about, tag};
use super::contexts::{about, settings, tag};
use super::contexts::{category, user};
use crate::common::AppData;

Expand All @@ -22,7 +22,8 @@ pub fn router(app_data: Arc<AppData>) -> Router {
.nest("/about", about::routes::router(app_data.clone()))
.nest("/category", category::routes::router(app_data.clone()))
.nest("/tag", tag::routes::router_for_single_resources(app_data.clone()))
.nest("/tags", tag::routes::router_for_multiple_resources(app_data.clone()));
.nest("/tags", tag::routes::router_for_multiple_resources(app_data.clone()))
.nest("/settings", settings::routes::router(app_data.clone()));

Router::new()
.route("/", get(about_page_handler).with_state(app_data))
Expand Down
118 changes: 118 additions & 0 deletions tests/e2e/contexts/settings/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,121 @@ async fn it_should_allow_admins_to_update_all_the_settings() {
}
assert_eq!(response.status, 200);
}

mod with_axum_implementation {
use std::env;

use torrust_index_backend::web::api;

use crate::common::asserts::assert_json_ok;
use crate::common::client::Client;
use crate::common::contexts::settings::responses::{AllSettingsResponse, Public, PublicSettingsResponse, SiteNameResponse};
use crate::e2e::config::ENV_VAR_E2E_EXCLUDE_AXUM_IMPL;
use crate::e2e::contexts::user::steps::new_logged_in_admin;
use crate::e2e::environment::TestEnv;

#[tokio::test]
async fn it_should_allow_guests_to_get_the_public_settings() {
let mut env = TestEnv::new();
env.start(api::Implementation::Axum).await;

if env::var(ENV_VAR_E2E_EXCLUDE_AXUM_IMPL).is_ok() {
println!("Skipped");
return;
}

let client = Client::unauthenticated(&env.server_socket_addr().unwrap());

let response = client.get_public_settings().await;

let res: PublicSettingsResponse = serde_json::from_str(&response.body)
.unwrap_or_else(|_| panic!("response {:#?} should be a PublicSettingsResponse", response.body));

assert_eq!(
res.data,
Public {
website_name: env.server_settings().unwrap().website.name,
tracker_url: env.server_settings().unwrap().tracker.url,
tracker_mode: env.server_settings().unwrap().tracker.mode,
email_on_signup: env.server_settings().unwrap().auth.email_on_signup,
}
);

assert_json_ok(&response);
}

#[tokio::test]
async fn it_should_allow_guests_to_get_the_site_name() {
let mut env = TestEnv::new();
env.start(api::Implementation::Axum).await;

if env::var(ENV_VAR_E2E_EXCLUDE_AXUM_IMPL).is_ok() {
println!("Skipped");
return;
}

let client = Client::unauthenticated(&env.server_socket_addr().unwrap());

let response = client.get_site_name().await;

let res: SiteNameResponse = serde_json::from_str(&response.body).unwrap();

assert_eq!(res.data, "Torrust");

assert_json_ok(&response);
}

#[tokio::test]
async fn it_should_allow_admins_to_get_all_the_settings() {
let mut env = TestEnv::new();
env.start(api::Implementation::Axum).await;

if env::var(ENV_VAR_E2E_EXCLUDE_AXUM_IMPL).is_ok() {
println!("Skipped");
return;
}

let logged_in_admin = new_logged_in_admin(&env).await;
let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token);

let response = client.get_settings().await;

let res: AllSettingsResponse = serde_json::from_str(&response.body).unwrap();

assert_eq!(res.data, env.server_settings().unwrap());

assert_json_ok(&response);
}

#[tokio::test]
async fn it_should_allow_admins_to_update_all_the_settings() {
let mut env = TestEnv::new();
env.start(api::Implementation::Axum).await;

if env::var(ENV_VAR_E2E_EXCLUDE_AXUM_IMPL).is_ok() {
println!("Skipped");
return;
}

if !env.is_isolated() {
// This test can't be executed in a non-isolated environment because
// it will change the settings for all the other tests.
return;
}

let logged_in_admin = new_logged_in_admin(&env).await;
let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token);

let mut new_settings = env.server_settings().unwrap();

new_settings.website.name = "UPDATED NAME".to_string();

let response = client.update_settings(&new_settings).await;

let res: AllSettingsResponse = serde_json::from_str(&response.body).unwrap();

assert_eq!(res.data, new_settings);

assert_json_ok(&response);
}
}

0 comments on commit 619cc33

Please sign in to comment.