Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use WithRejection Extractor to Customize Rejection Payload JSONs #2440

Merged
merged 2 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions api-backend/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion api-backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[workspace]
members = [
"hartex-backend-driver",
"hartex-backend-extractors",
"hartex-backend-models",
"hartex-backend-routes",
]
Expand Down
9 changes: 4 additions & 5 deletions api-backend/hartex-backend-driver/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,15 @@ pub async fn main() -> miette::Result<()> {
let api_pgsql_url = env::var("API_PGSQL_URL").into_diagnostic()?;

log::debug!("building database connection pool");
let manager = PostgresConnectionManager::new_from_stringlike(api_pgsql_url, NoTls).into_diagnostic()?;
let manager =
PostgresConnectionManager::new_from_stringlike(api_pgsql_url, NoTls).into_diagnostic()?;
let pool = Pool::builder().build(manager).await.into_diagnostic()?;

log::debug!("starting axum server");
let (app, mut openapi) = OpenApiRouter::new()
.layer(TraceLayer::new_for_http())
.layer(TimeoutLayer::new(Duration::from_secs(30)))
.routes(routes!(
hartex_backend_routes::uptime::get_uptime
))
.routes(routes!(hartex_backend_routes::uptime::get_uptime))
.with_state(pool)
.split_for_parts();

Expand Down Expand Up @@ -123,7 +122,7 @@ async fn shutdown() {
.recv()
.await;
};

#[cfg(not(unix))]
let terminate = future::pending::<()>();

Expand Down
15 changes: 0 additions & 15 deletions api-backend/hartex-backend-extractors/Cargo.toml

This file was deleted.

29 changes: 0 additions & 29 deletions api-backend/hartex-backend-extractors/src/lib.rs

This file was deleted.

49 changes: 35 additions & 14 deletions api-backend/hartex-backend-models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@
#![deny(unsafe_code)]
#![deny(warnings)]

use axum::http::StatusCode;
use axum::Json;
use serde::Deserialize;
use serde::Serialize;

pub use hartex_discord_configuration_models as config;
pub mod uptime;

/// An API response object.
///
/// This is the object returned by a certain API endpoint.
#[derive(Deserialize, Serialize)]
pub struct Response<T> {
code: u16,
pub code: u16,
message: String,
data: Option<T>,
}
Expand All @@ -49,22 +49,43 @@ impl<'a, T> Response<T>
where
T: Clone + Deserialize<'a>,
{
#[allow(clippy::missing_panics_doc)]
pub fn from_code_with_data(code: StatusCode, data: T) -> (StatusCode, Json<Response<T>>) {
let code_display = code.to_string();
let part = code_display.split_once(' ').unwrap().1;

(
code,
Json(Self {
code: code.as_u16(),
message: part.to_lowercase(),
data: Some(data),
}),
)
}

/// Constructs a response object with a status code of 500 and its corresponding message.
pub fn internal_server_error() -> Json<Response<T>> {
Json(Self {
code: 500,
message: String::from("internal server error"),
data: None,
})
pub fn internal_server_error() -> (StatusCode, Json<Response<T>>) {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(Self {
code: 500,
message: String::from("internal server error"),
data: None,
}),
)
}

/// Constructs a response object with a status code of 200 and its corresponding message.
pub fn ok(value: T) -> Json<Response<T>> {
Json(Self {
code: 200,
message: String::from("ok"),
data: Some(value),
})
pub fn ok(value: T) -> (StatusCode, Json<Response<T>>) {
(
StatusCode::OK,
Json(Self {
code: 200,
message: String::from("ok"),
data: Some(value),
}),
)
}
}

Expand Down
28 changes: 26 additions & 2 deletions api-backend/hartex-backend-models/src/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
//!
//! Models for the uptime API specification V2 of the backend.

use axum::extract::rejection::QueryRejection;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::response::Response;
use serde::Deserialize;
use serde::Serialize;
use utoipa::IntoParams;
Expand Down Expand Up @@ -52,7 +56,27 @@ impl UptimeQuery {
}
}

/// A response to an uptime query.
pub struct UptimeQueryRejection {
status_code: StatusCode,
data_message: String,
}

impl From<QueryRejection> for UptimeQueryRejection {
fn from(value: QueryRejection) -> Self {
Self {
status_code: value.status(),
data_message: value.body_text().to_lowercase(),
}
}
}

impl IntoResponse for UptimeQueryRejection {
fn into_response(self) -> Response {
crate::Response::from_code_with_data(self.status_code, self.data_message).into_response()
}
}

/// The uptime of the specified component.
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Deserialize, Serialize, ToSchema)]
pub struct UptimeResponse {
Expand All @@ -66,7 +90,7 @@ impl UptimeResponse {
Self { start_timestamp }
}

/// The start timestamp of the uptime entry.
/// The start timestamp of the uptime data.
#[must_use]
pub fn start_timestamp(&self) -> u128 {
self.start_timestamp
Expand Down
1 change: 1 addition & 0 deletions api-backend/hartex-backend-routes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ hartex_database_queries = { path = "../../database/hartex-database-queries" }
hartex_log = { path = "../../rust-utilities/hartex-log" }

axum = { version = "0.7.7", features = ["json", "macros"] }
axum-extra = "0.9.4"
bb8-postgres = "0.8.1"
serde_json = "1.0.128"
time = "0.3.36"
Expand Down
44 changes: 12 additions & 32 deletions api-backend/hartex-backend-routes/src/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
/// # Uptime Routes
///
/// Routes interacting with the uptime API.

use axum::extract::Query;
use axum::extract::State;
use axum::http::StatusCode;
use axum::Json;
use axum_extra::extract::WithRejection;
use bb8_postgres::bb8::Pool;
use bb8_postgres::tokio_postgres::GenericClient;
use bb8_postgres::tokio_postgres::NoTls;
use bb8_postgres::PostgresConnectionManager;
use hartex_backend_models::uptime::UptimeQuery;
use hartex_backend_models::uptime::UptimeQueryRejection;
use hartex_backend_models::uptime::UptimeResponse;
use hartex_backend_models::uptime::UptimeUpdate;
use hartex_backend_models::Response;
Expand All @@ -53,15 +54,12 @@ use time::OffsetDateTime;
)]
pub async fn get_uptime(
State(pool): State<Pool<PostgresConnectionManager<NoTls>>>,
Query(query): Query<UptimeQuery>,
WithRejection(Query(query), _): WithRejection<Query<UptimeQuery>, UptimeQueryRejection>,
) -> (StatusCode, Json<Response<UptimeResponse>>) {
log::trace!("retrieving connection from database pool");
let result = pool.get().await;
if result.is_err() {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Response::internal_server_error(),
);
return Response::internal_server_error();
}

let connection = result.unwrap();
Expand All @@ -77,19 +75,13 @@ pub async fn get_uptime(
if result.is_err() {
log::error!("{:?}", result.unwrap_err());

return (
StatusCode::INTERNAL_SERVER_ERROR,
Response::internal_server_error(),
);
return Response::internal_server_error();
}
let data = result.unwrap();

(
StatusCode::OK,
Response::ok(UptimeResponse::with_start_timestamp(
data.timestamp.unix_timestamp() as u128,
)),
)
Response::ok(UptimeResponse::with_start_timestamp(
data.timestamp.unix_timestamp() as u128,
))
}

/// # `PATCH /stats/uptime`
Expand All @@ -106,10 +98,7 @@ pub async fn patch_uptime(
log::trace!("retrieving connection from database pool");
let result = pool.get().await;
if result.is_err() {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Response::internal_server_error(),
);
return Response::internal_server_error();
}

let connection = result.unwrap();
Expand All @@ -119,24 +108,15 @@ pub async fn patch_uptime(
let Ok(timestamp) = OffsetDateTime::from_unix_timestamp(query.start_timestamp() as i64) else {
// FIXME: return a better status code as the timestamp is out of range if this branch is reached
// just 500 for now
return (
StatusCode::INTERNAL_SERVER_ERROR,
Response::internal_server_error(),
);
return Response::internal_server_error();
};
let result = start_timestamp_upsert()
.bind(client, &query.component_name(), &timestamp)
.await;

if result.is_err() {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Response::internal_server_error(),
);
return Response::internal_server_error();
}

(
StatusCode::OK,
Response::ok(()),
)
Response::ok(())
}
Loading