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

404 Response: Attempt to Use Either #2446

Merged
merged 19 commits into from
Nov 11, 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
33 changes: 19 additions & 14 deletions api-backend/Cargo.lock

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

1 change: 1 addition & 0 deletions api-backend/hartex-backend-models/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rust-version = "1.83.0"
hartex_discord_configuration_models = { path = "../../discord-frontend/hartex-discord-configuration-models" }

axum = "0.7.7"
either = { version = "1.13.0", features = ["serde"] }
serde = { version = "1.0.210", features = ["derive"] }
utoipa = "5.1.3"
utoipa-axum = "0.1.2"
Expand Down
57 changes: 32 additions & 25 deletions api-backend/hartex-backend-models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

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

Expand All @@ -39,18 +40,23 @@ pub mod uptime;
///
/// This is the object returned by a certain API endpoint.
#[derive(Deserialize, Serialize)]
pub struct Response<T> {
pub struct Response<T, R> {
pub code: u16,
message: String,
data: Option<T>,
data: Either<Option<T>, R>,
}

impl<'a, T> Response<T>
impl<'a, T, R> Response<T, R>
where
T: Clone + Deserialize<'a>,
R: Clone + Deserialize<'a>,
{
/// Constructs a response object from a status code and data.
#[allow(clippy::missing_panics_doc)]
pub fn from_code_with_data(code: StatusCode, data: T) -> (StatusCode, Json<Response<T>>) {
pub fn from_code_with_data(
code: StatusCode,
data: Either<Option<T>, R>,
) -> (StatusCode, Json<Response<T, R>>) {
let code_display = code.to_string();
let part = code_display.split_once(' ').unwrap().1;

Expand All @@ -59,39 +65,40 @@ where
Json(Self {
code: code.as_u16(),
message: part.to_lowercase(),
data: Some(data),
data,
}),
)
}

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

/// Constructs a response object with a status code of 500 and its corresponding message.
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,
}),
)
pub fn internal_server_error() -> (StatusCode, Json<Response<T, R>>) {
Self::from_code_with_data(StatusCode::INTERNAL_SERVER_ERROR, Either::Left(None))
}
}

/// Constructs a response object with a status code of 200 and its corresponding message.
pub fn ok(value: T) -> (StatusCode, Json<Response<T>>) {
(
StatusCode::OK,
Json(Self {
code: 200,
message: String::from("ok"),
data: Some(value),
}),
impl<'a, T> Response<T, String>
where
T: Clone + Deserialize<'a>,
{
/// Constructs a response object with a status code of 404 and its corresponding message.
#[allow(clippy::needless_pass_by_value)]
pub fn not_found(component_missing: String) -> (StatusCode, Json<Response<T, String>>) {
Self::from_code_with_data(
StatusCode::NOT_FOUND,
Either::Right(format!("{component_missing} not found")),
)
}
}

impl<'a, T> Response<T>
impl<'a, T, R> Response<T, R>
where
T: Clone + Deserialize<'a>,
R: Clone + Deserialize<'a>,
{
/// The status code of the response.
pub fn code(&self) -> u16 {
Expand All @@ -104,7 +111,7 @@ where
}

/// The data of the response.
pub fn data(&self) -> Option<T> {
pub fn data(&self) -> Either<Option<T>, R> {
self.data.clone()
}
}
7 changes: 6 additions & 1 deletion api-backend/hartex-backend-models/src/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use axum::extract::rejection::QueryRejection;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::response::Response;
use either::Either;
use serde::Deserialize;
use serde::Serialize;
use utoipa::IntoParams;
Expand Down Expand Up @@ -72,7 +73,11 @@ impl From<QueryRejection> for UptimeQueryRejection {

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

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 @@ -20,6 +20,7 @@ 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"
futures-util = "0.3.31"
serde_json = "1.0.128"
time = "0.3.36"
utoipa = "5.1.3"
Expand Down
28 changes: 20 additions & 8 deletions api-backend/hartex-backend-routes/src/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use bb8_postgres::bb8::Pool;
use bb8_postgres::tokio_postgres::GenericClient;
use bb8_postgres::tokio_postgres::NoTls;
use bb8_postgres::PostgresConnectionManager;
use futures_util::stream::TryStreamExt;
use hartex_backend_models::uptime::UptimeQuery;
use hartex_backend_models::uptime::UptimeQueryRejection;
use hartex_backend_models::uptime::UptimeResponse;
Expand All @@ -55,7 +56,7 @@ use time::OffsetDateTime;
pub async fn get_uptime(
State(pool): State<Pool<PostgresConnectionManager<NoTls>>>,
WithRejection(Query(query), _): WithRejection<Query<UptimeQuery>, UptimeQueryRejection>,
) -> (StatusCode, Json<Response<UptimeResponse>>) {
) -> (StatusCode, Json<Response<UptimeResponse, String>>) {
log::trace!("retrieving connection from database pool");
let result = pool.get().await;
if result.is_err() {
Expand All @@ -66,21 +67,32 @@ pub async fn get_uptime(
let client = connection.client();

log::trace!("querying timestamp");
let result = select_start_timestamp_by_component()
.bind(client, &query.component_name())
.one()
let name = query.component_name();
let mut query = select_start_timestamp_by_component();
let result = query
.bind(client, &name)
.iter()
.await;

// FIXME: figure out whether the data is actually not found and return 404
if result.is_err() {
return Response::internal_server_error();
}

let iterator = result.unwrap();
let result = iterator.try_collect::<Vec<_>>().await;
if result.is_err() {
log::error!("{:?}", result.unwrap_err());

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

let vec = result.unwrap();
if vec.is_empty() {
return Response::not_found(String::from("component"));
}

Response::ok(UptimeResponse::with_start_timestamp(
data.timestamp.unix_timestamp() as u128,
vec[0].timestamp.unix_timestamp() as u128,
))
}

Expand All @@ -94,7 +106,7 @@ pub async fn get_uptime(
pub async fn patch_uptime(
State(pool): State<Pool<PostgresConnectionManager<NoTls>>>,
Json(query): Json<UptimeUpdate>,
) -> (StatusCode, Json<Response<()>>) {
) -> (StatusCode, Json<Response<(), String>>) {
log::trace!("retrieving connection from database pool");
let result = pool.get().await;
if result.is_err() {
Expand Down
4 changes: 4 additions & 0 deletions discord-frontend/Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ pub async fn execute(
let result = sender.send_request(request).await.into_diagnostic()?;
log::debug!("deserializing result");
let body = result.collect().await.into_diagnostic()?.aggregate();
let response: Response<UptimeResponse> =
let response: Response<UptimeResponse, String> =
serde_json::from_reader(body.reader()).into_diagnostic()?;

let latency = now.elapsed().into_diagnostic()?.as_millis();

let data = response.data();
let timestamp = data
.left()
.flatten()
.ok_or(Report::msg("failed to obtain uptime data"))?
.start_timestamp();

Expand Down
Loading