populated build-info endpoint

This commit is contained in:
Jędrzej Stuczyński
2023-09-21 14:28:59 +01:00
parent 5de218bca9
commit a1aff09b91
15 changed files with 110 additions and 30 deletions
+1
View File
@@ -171,6 +171,7 @@ tokio-tungstenite = "0.20.1"
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
utoipa = "3.5.0"
url = "2.4"
zeroize = "1.6.0"
+2
View File
@@ -29,6 +29,7 @@ opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
"isahc_collector_client",
] }
tracing-opentelemetry = { version = "0.19.0", optional = true }
utoipa = { workspace = true, optional = true }
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
@@ -42,6 +43,7 @@ vergen = { version = "=7.4.3", default-features = false, features = [
[features]
default = []
openapi = ["utoipa"]
output_format = ["serde_json"]
tracing = [
"tracing-subscriber",
@@ -81,6 +81,7 @@ impl BinaryBuildInformation {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct BinaryBuildInformationOwned {
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
pub binary_name: String,
+6 -2
View File
@@ -15,16 +15,20 @@ anyhow = { workspace = true }
bytes = "1.5.0"
serde = { workspace = true, features = ["derive"] }
serde_yaml = "0.9.25"
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
# HTTP API:
axum = { workspace = true }
# useful for `#[axum_macros::debug_handler]`
#axum-macros = "0.3.8"
mime = "0.3.17"
hyper = { workspace = true }
tower = { version = "0.4.13" }
tower-http = { version = "0.4.4", features = ["fs"] }
utoipa = { version = "3.5.0", features = ["axum_extras"] }
utoipa = { workspace = true, features = ["axum_extras"] }
utoipa-swagger-ui = { version = "3.1.5", features = ["axum"] }
# if we ever wanted redoc/rapidoc bridges:
@@ -32,4 +36,4 @@ utoipa-swagger-ui = { version = "3.1.5", features = ["axum"] }
#utoipa-rapidoc = { version = "0.1.0", features = ["axum"] }
nym-bin-common = { path = "../common/bin-common" }
nym-bin-common = { path = "../common/bin-common", features = ["openapi"] }
+32 -6
View File
@@ -1,11 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::http::{header, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::{Json, Router};
use bytes::{BufMut, BytesMut};
use serde::{Deserialize, Serialize};
use utoipa::{IntoParams, ToSchema};
pub mod v1;
@@ -19,13 +21,31 @@ pub struct Config {
pub v1_config: v1::Config,
}
pub(super) fn routes(config: Config) -> Router {
Router::new().nest_service(routes::V1, v1::routes(config.v1_config))
pub(super) fn routes(config: Config) -> Router<AppState> {
Router::new().nest(routes::V1, v1::routes(config.v1_config))
// .nest(routes::SWAGGER, openapi::route())
// .nest(routes::SWAGGER, openapi::route())
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
#[derive(Debug, Clone, ToSchema)]
pub enum FormattedResponse<T> {
Json(Json<T>),
Yaml(Yaml<T>),
}
impl<T> IntoResponse for FormattedResponse<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
match self {
FormattedResponse::Json(json_response) => json_response.into_response(),
FormattedResponse::Yaml(yaml_response) => yaml_response.into_response(),
}
}
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum Output {
#[default]
@@ -33,11 +53,17 @@ pub enum Output {
Yaml,
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema)]
#[serde(default)]
pub struct OutputParams {
pub output: Option<Output>,
}
impl Output {
pub fn to_response<T: Serialize + 'static>(self, data: T) -> Box<dyn IntoResponse> {
pub fn to_response<T: Serialize>(self, data: T) -> FormattedResponse<T> {
match self {
Output::Json => Box::new(Json(data)),
Output::Yaml => Box::new(Yaml(data)),
Output::Json => FormattedResponse::Json(Json(data)),
Output::Yaml => FormattedResponse::Yaml(Yaml(data)),
}
}
}
+22 -11
View File
@@ -1,22 +1,33 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::router::api::Output;
use axum::extract::Query;
use crate::http::router::api::{FormattedResponse, OutputParams};
use crate::http::state::AppState;
use axum::extract::{Query, State};
use axum::response::IntoResponse;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
/// Description of the path
#[utoipa::path(
get,
path = "/",
path = "/build-info",
context_path = "/api/v1",
responses(
(status=200, description ="", body = ())
(status=200, content(
("application/json" = BinaryBuildInformationOwned),
("application/yaml" = BinaryBuildInformationOwned)
))
),
params(
("output" = Option<Output>, Query, description = "")
)
params(OutputParams)
)]
pub async fn build_info(Query(output): Query<Option<Output>>) -> impl IntoResponse {
let output = output.unwrap_or_default();
todo!()
pub async fn build_info(
State(state): State<AppState>,
Query(output): Query<OutputParams>,
) -> FormattedResponse<BinaryBuildInformationOwned> {
let output = output.output.unwrap_or_default();
// todo!()
// TODO: get rid of the clone since it's getting serialized anyway
output.to_response(state.build_information.clone())
}
pub struct BuildInfoResponse(BinaryBuildInformationOwned);
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::http::StatusCode;
use axum::routing::get;
use axum::Router;
@@ -8,6 +9,6 @@ use axum::Router;
#[derive(Debug, Clone, Default)]
pub struct Config {}
pub(crate) fn routes(_config: Config) -> Router {
pub(crate) fn routes(_config: Config) -> Router<AppState> {
Router::new().route("/", get(|| async { StatusCode::NOT_IMPLEMENTED }))
}
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::http::StatusCode;
use axum::routing::get;
use axum::Router;
@@ -8,6 +9,6 @@ use axum::Router;
#[derive(Debug, Clone, Default)]
pub struct Config {}
pub(crate) fn routes(_config: Config) -> Router {
pub(crate) fn routes(_config: Config) -> Router<AppState> {
Router::new().route("/", get(|| async { StatusCode::NOT_IMPLEMENTED }))
}
+2 -1
View File
@@ -3,6 +3,7 @@
use crate::http::router::api::v1::build_info::build_info;
use crate::http::router::api::v1::roles::roles;
use crate::http::state::AppState;
use axum::routing::get;
use axum::Router;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
@@ -30,7 +31,7 @@ pub struct Config {
pub network_requester: network_requester::Config,
}
pub(super) fn routes(config: Config) -> Router {
pub(super) fn routes(config: Config) -> Router<AppState> {
Router::new()
.nest(routes::GATEWAY, gateway::routes(config.gateway))
.nest(routes::MIXNODE, mixnode::routes(config.mixnide))
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::http::StatusCode;
use axum::routing::get;
use axum::Router;
@@ -8,6 +9,6 @@ use axum::Router;
#[derive(Debug, Clone, Default)]
pub struct Config {}
pub(crate) fn routes(_config: Config) -> Router {
pub(crate) fn routes(_config: Config) -> Router<AppState> {
Router::new().route("/", get(|| async { StatusCode::NOT_IMPLEMENTED }))
}
+9 -2
View File
@@ -1,15 +1,22 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::router::api;
use crate::http::state::AppState;
use axum::Router;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
#[derive(OpenApi)]
#[openapi(info(title = "NymNode API"))]
#[openapi(
info(title = "NymNode API"),
paths(api::v1::build_info::build_info,),
components(schemas(api::Output, api::OutputParams, BinaryBuildInformationOwned))
)]
pub(crate) struct ApiDoc;
pub(crate) fn route() -> Router {
pub(crate) fn route() -> Router<AppState> {
// provide absolute path to the openapi.json
let config = utoipa_swagger_ui::Config::from("/api/v1/api-docs/openapi.json");
SwaggerUi::new("/swagger")
+2 -1
View File
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::routing::get;
use axum::Router;
use std::path::PathBuf;
@@ -11,7 +12,7 @@ pub struct Config {
pub assets_path: Option<PathBuf>,
}
pub(super) fn routes(config: Config) -> Router {
pub(super) fn routes(config: Config) -> Router<AppState> {
if let Some(assets) = config.assets_path {
Router::new().nest_service("/", ServeDir::new(assets))
} else {
+3 -1
View File
@@ -3,6 +3,7 @@
use crate::error::NymNodeError;
use crate::http::middleware::logging;
use crate::http::state::AppState;
use axum::routing::IntoMakeService;
use axum::Router;
use hyper::server::conn::AddrIncoming;
@@ -47,7 +48,8 @@ impl NymNodeRouter {
.nest(routes::LANDING_PAGE, landing_page::routes(config.landing))
.nest(routes::POLICY, policy::routes(config.policy))
.nest(routes::API, api::routes(config.api))
.layer(axum::middleware::from_fn(logging::logger)),
.layer(axum::middleware::from_fn(logging::logger))
.with_state(AppState::new_dummy()),
}
}
+2 -1
View File
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::routing::get;
use axum::Router;
use std::path::PathBuf;
@@ -11,7 +12,7 @@ pub struct Config {
pub assets_path: Option<PathBuf>,
}
pub(super) fn routes(config: Config) -> Router {
pub(super) fn routes(config: Config) -> Router<AppState> {
if let Some(assets) = config.assets_path {
Router::new().nest_service("/", ServeDir::new(assets))
} else {
+22 -2
View File
@@ -1,16 +1,36 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_bin_common::bin_info_owned;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use std::ops::Deref;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub(crate) struct AppState {
pub struct AppState {
inner: Arc<AppStateInner>,
}
impl AppState {
pub fn new_dummy() -> Self {
AppState {
inner: Arc::new(AppStateInner {
build_information: bin_info_owned!(),
}),
}
}
}
impl Deref for AppState {
type Target = AppStateInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Debug)]
struct AppStateInner {
pub struct AppStateInner {
// TODO: split it based on routes?
pub(crate) build_information: BinaryBuildInformationOwned,
}