cat_gateway/service/
poem_service.rs

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
//! Poem Service for cat-gateway service endpoints.
//!
//! This provides only the primary entrypoint to the service.

use poem::{
    endpoint::PrometheusExporter,
    listener::TcpListener,
    middleware::{CatchPanic, Compression, Cors},
    web::CompressionLevel,
    Endpoint, EndpointExt, Route,
};

use crate::{
    service::{
        api::mk_api,
        docs::{docs, favicon},
        utilities::{
            catch_panic::{set_panic_hook, ServicePanicHandler},
            middleware::tracing_mw::{init_prometheus, Tracing},
        },
    },
    settings::Settings,
};

/// This exists to allow us to add extra routes to the service for testing purposes.
fn mk_app(base_route: Option<Route>) -> impl Endpoint {
    // Get the base route if defined, or a new route if not.
    let base_route = match base_route {
        Some(route) => route,
        None => Route::new(),
    };

    let api_service = mk_api();
    let docs = docs(&api_service);

    let prometheus_registry = init_prometheus();

    base_route
        .nest(Settings::api_url_prefix(), api_service)
        .nest("/docs", docs)
        .nest("/metrics", PrometheusExporter::new(prometheus_registry))
        .nest("/favicon.ico", favicon())
        .with(Cors::new())
        .with(Compression::new().with_quality(CompressionLevel::Fastest))
        .with(CatchPanic::new().with_handler(ServicePanicHandler))
        .with(Tracing)
}

/// Get the API docs as a string in the JSON format.
pub(crate) fn get_app_docs() -> String {
    let api_service = mk_api();
    api_service.spec()
}

/// Run the Poem Service
///
/// This provides only the primary entrypoint to the service.
///
/// # Arguments
///
/// *`settings`: &`DocsSetting` - settings for docs
///
/// # Errors
///
/// * `Error::CannotRunService` - cannot run the service
/// * `Error::EventDbError` - cannot connect to the event db
/// * `Error::IoError` - An IO error has occurred.
pub(crate) async fn run() -> anyhow::Result<()> {
    // The address to listen on
    tracing::info!(
        ServiceAddr = Settings::bound_address().to_string(),
        "Starting Cat-Gateway API Service ..."
    );

    // Set a custom panic hook, so we can catch panics and not crash the service.
    // And also get data from the panic so we can log it.
    // Panics will cause a 500 to be sent with minimal information we can use to
    // help find them in the logs if they happen in production.
    set_panic_hook();

    let app = mk_app(None);

    Ok(
        poem::Server::new(TcpListener::bind(Settings::bound_address()))
            .run(app)
            .await?,
    )
}

#[cfg(test)]
pub(crate) mod tests {
    // Poem TEST violates our License by using Copyleft libraries.
    // use poem::test::TestClient;
    //
    // use super::*;
    //
    // pub(crate) fn mk_test_app(state: &Arc<State>) -> TestClient<impl Endpoint> {
    // let app = mk_app(vec![], None, state);
    // TestClient::new(app)
    // }
}