aboutsummaryrefslogtreecommitdiffstats
path: root/crates/sellershut/src/server/api/routes/logs/mod.rs
blob: 9ea0a39b994e509fc2fb9851296113136f2737a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use axum::{Json, extract::State, http::StatusCode};
use serde::Deserialize;
use tracing::warn;
use utoipa::ToSchema;

use crate::state::AppState;

#[derive(Deserialize, Debug, Clone, ToSchema)]
/// Log level
#[serde(rename_all = "camelCase")]
pub struct LogLevel {
    #[schema(examples("info", "trace", "warden=debug,tower_http=debug,axum::rejection=trace"))]
    log_level: String,
}

/// Update log level
#[utoipa::path(
    patch,
    responses(
        (
            status = 200,
            description = "Server's log level has been updated",
            headers(
                ("x-request-id", description = "Request identifier")
            )
        ),
        (
            status = 400,
            description = "Invalid log level",
            headers(
                ("x-request-id", description = "Request identifier")
            )
        ),
    ),
    operation_id = "log_update", // https://github.com/juhaku/utoipa/issues/1170
    path = "/logging",
    tag = super::CONFIG,
    request_body(
        content = LogLevel
    )
)]
pub async fn reload(State(state): State<AppState>, Json(body): Json<LogLevel>) -> StatusCode {
    if let Ok(value) = body.log_level.parse::<tracing_subscriber::EnvFilter>() {
        match state.log_handle.reload(value) {
            Ok(_) => StatusCode::OK,
            Err(e) => {
                warn!("{e:?}");
                StatusCode::INTERNAL_SERVER_ERROR
            }
        }
    } else {
        StatusCode::BAD_REQUEST
    }
}

#[cfg(test)]
mod tests {
    use axum::{
        Router,
        body::Body,
        http::{Request, StatusCode, header},
    };

    use anyhow::Result;
    use tower::ServiceExt;

    use crate::server::{self};

    async fn check(
        app: Router,
        method: &str,
        body: String,
        expected_result: StatusCode,
    ) -> Result<()> {
        let response = app
            .oneshot(
                Request::builder()
                    .method(method)
                    .header(header::CONTENT_TYPE, "application/json")
                    .uri("/api/logging")
                    .body(Body::from(body))?,
            )
            .await?;
        let actual_result = response.status();
        assert_eq!(expected_result, actual_result);
        Ok(())
    }

    #[tokio::test]
    async fn log_update() -> Result<()> {
        let app = server::boostrap::test_app().await;

        let info = serde_json::json!({
          "logLevel": "info",
        });

        check(
            app.clone(),
            "GET",
            info.to_string(),
            StatusCode::METHOD_NOT_ALLOWED,
        )
        .await?;

        check(app.clone(), "PATCH", info.to_string(), StatusCode::OK).await?;
        Ok(())
    }
}