missing swagger data + using closure capture for immutable state

This commit is contained in:
Jędrzej Stuczyński
2023-09-22 13:53:50 +01:00
parent eda8b6bf85
commit 0489e5aa65
19 changed files with 277 additions and 77 deletions
+13 -2
View File
@@ -12,6 +12,7 @@ use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_bin_common::output_format::OutputFormat;
use nym_bin_common::{bin_info, bin_info_owned};
use nym_network_defaults::setup_env;
use nym_node::http::api::v1::roles::NodeRoles;
use std::error::Error;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
@@ -56,8 +57,18 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
api: nym_node::http::api::Config {
v1_config: nym_node::http::api::v1::Config {
build_information: bin_info_owned!(),
gateway: Default::default(),
mixnide: Default::default(),
roles: NodeRoles {
mixnode_enabled: false,
gateway_enabled: false,
network_requester_enabled: false,
},
gateway: nym_node::http::api::v1::gateway::Config {
details: Some(nym_node::http::api::v1::gateway::types::Gateway {
encoded_identity_key: "aaa".to_string(),
encoded_sphinx_key: "bbb".to_string(),
}),
},
mixnode: Default::default(),
network_requester: Default::default(),
},
},
+1
View File
@@ -13,6 +13,7 @@ license.workspace = true
[dependencies]
anyhow = { workspace = true }
bytes = "1.5.0"
colored = "2"
serde = { workspace = true, features = ["derive"] }
serde_yaml = "0.9.25"
serde_json = { workspace = true }
+12 -4
View File
@@ -6,13 +6,14 @@ use axum::{
middleware::Next,
response::IntoResponse,
};
use colored::*;
use hyper::header::{HOST, USER_AGENT};
use tracing::{debug, info};
use tracing::info;
/// Simple logger for requests
pub async fn logger<B>(req: Request<B>, next: Next<B>) -> impl IntoResponse {
let method = req.method().to_string();
let uri = req.uri().to_string();
let method = req.method().to_string().green();
let uri = req.uri().to_string().blue();
let agent = header_map(
req.headers().get(USER_AGENT),
"Unknown User Agent".to_string(),
@@ -22,8 +23,15 @@ pub async fn logger<B>(req: Request<B>, next: Next<B>) -> impl IntoResponse {
let res = next.run(req).await;
let status = res.status();
let print_status = if status.is_client_error() || status.is_server_error() {
status.to_string().red()
} else if status.is_success() {
status.to_string().green()
} else {
status.to_string().yellow()
};
info!("[{host}] {method} '{uri}': {status} / agent: {agent}");
info!("[{host}] {method} '{uri}': {print_status} / agent: {agent}");
res
}
-1
View File
@@ -13,7 +13,6 @@ pub mod v1;
pub(crate) mod routes {
pub(crate) const V1: &str = "/v1";
pub(crate) const SWAGGER: &str = "/swagger";
}
#[derive(Debug, Clone)]
@@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::http::router::api::{FormattedResponse, OutputParams};
use crate::http::state::AppState;
use axum::extract::{Query, State};
use axum::extract::Query;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
/// Returns build metadata of the binary running the API
@@ -11,6 +10,7 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned;
get,
path = "/build-info",
context_path = "/api/v1",
tag = "Base",
responses(
(status = 200, content(
("application/json" = BinaryBuildInformationOwned),
@@ -20,12 +20,11 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned;
params(OutputParams)
)]
pub(crate) async fn build_info(
State(state): State<AppState>,
build_information: BinaryBuildInformationOwned,
Query(output): Query<OutputParams>,
) -> BuildInfoResponse {
let output = output.output.unwrap_or_default();
// TODO: get rid of the clone since it's getting serialized anyway
output.to_response(state.build_information.clone())
output.to_response(build_information)
}
pub type BuildInfoResponse = FormattedResponse<BinaryBuildInformationOwned>;
+13 -4
View File
@@ -6,14 +6,23 @@ use axum::routing::get;
use axum::Router;
pub mod root;
pub mod types;
pub(crate) mod routes {
pub(crate) const ROOT: &str = "/";
}
#[derive(Debug, Clone, Default)]
pub struct Config {}
pub(crate) fn routes(_config: Config) -> Router<AppState> {
Router::new().route(routes::ROOT, get(root::root_gateway))
pub struct Config {
pub details: Option<types::Gateway>,
}
pub(crate) fn routes(config: Config) -> Router<AppState> {
Router::new().route(
routes::ROOT,
get({
let gateway_details = config.details;
move |query| root::root_gateway(gateway_details, query)
}),
)
}
@@ -1,24 +1,33 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::NymNodeError;
use crate::http::router::api::OutputParams;
use crate::http::api::v1::gateway::types::Gateway;
use crate::http::router::api::{FormattedResponse, OutputParams};
use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::IntoResponse;
/// Returns root gateway information
#[utoipa::path(
get,
path = "",
context_path = "/api/v1/gateway",
tag = "Gateway",
responses(
(status = 501, description = "the endpoint hasn't been implemented yet")
(status = 501, description = "the endpoint hasn't been implemented yet"),
(status = 200, content(
("application/json" = Gateway),
("application/yaml" = Gateway)
))
),
params(OutputParams)
)]
pub(crate) async fn root_gateway(Query(_output): Query<OutputParams>) -> impl IntoResponse {
(
StatusCode::NOT_IMPLEMENTED,
NymNodeError::Unimplemented.to_string(),
)
pub(crate) async fn root_gateway(
details: Option<Gateway>,
Query(output): Query<OutputParams>,
) -> Result<GatewayResponse, StatusCode> {
let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(details))
}
pub type GatewayResponse = FormattedResponse<Gateway>;
@@ -0,0 +1,15 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::Serialize;
use utoipa::ToSchema;
#[derive(Serialize, Debug, Clone, ToSchema)]
pub struct Gateway {
/// Base58 encoded ed25519 EdDSA public key of the gateway used for deriving shared keys with clients
/// and for signing any messages
pub encoded_identity_key: String,
/// Base58-encoded x25519 public key used for sphinx key derivation.
pub encoded_sphinx_key: String,
}
+19 -5
View File
@@ -2,13 +2,27 @@
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::http::StatusCode;
use axum::routing::get;
use axum::Router;
#[derive(Debug, Clone, Default)]
pub struct Config {}
pub mod root;
pub mod types;
pub(crate) fn routes(_config: Config) -> Router<AppState> {
Router::new().route("/", get(|| async { StatusCode::NOT_IMPLEMENTED }))
pub(crate) mod routes {
pub(crate) const ROOT: &str = "/";
}
#[derive(Debug, Clone, Default)]
pub struct Config {
pub details: Option<types::Mixnode>,
}
pub(crate) fn routes(config: Config) -> Router<AppState> {
Router::new().route(
routes::ROOT,
get({
let mixnode_details = config.details;
move |query| root::root_mixnode(mixnode_details, query)
}),
)
}
@@ -0,0 +1,33 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::api::v1::mixnode::types::Mixnode;
use crate::http::router::api::{FormattedResponse, OutputParams};
use axum::extract::Query;
use axum::http::StatusCode;
/// Returns root mixnode information
#[utoipa::path(
get,
path = "",
context_path = "/api/v1/mixnode",
tag = "Mixnode",
responses(
(status = 501, description = "the endpoint hasn't been implemented yet"),
(status = 200, content(
("application/json" = Mixnode),
("application/yaml" = Mixnode)
))
),
params(OutputParams)
)]
pub(crate) async fn root_mixnode(
details: Option<Mixnode>,
Query(output): Query<OutputParams>,
) -> Result<MixnodeResponse, StatusCode> {
let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(details))
}
pub type MixnodeResponse = FormattedResponse<Mixnode>;
@@ -0,0 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::Serialize;
use utoipa::ToSchema;
#[derive(Serialize, Debug, Clone, ToSchema)]
pub struct Mixnode {
/// Base58 encoded ed25519 EdDSA public key of the mixnode.
pub encoded_identity_key: String,
/// Base58-encoded x25519 public key used for sphinx key derivation.
pub encoded_sphinx_key: String,
}
+18 -4
View File
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::api::v1::roles::NodeRoles;
use crate::http::router::api::v1::build_info::build_info;
use crate::http::router::api::v1::roles::roles;
use crate::http::state::AppState;
@@ -27,20 +28,33 @@ pub(crate) mod routes {
#[derive(Debug, Clone)]
pub struct Config {
pub build_information: BinaryBuildInformationOwned,
pub roles: NodeRoles,
pub gateway: gateway::Config,
pub mixnide: mixnode::Config,
pub mixnode: mixnode::Config,
pub network_requester: network_requester::Config,
}
pub(super) fn routes(config: Config) -> Router<AppState> {
Router::new()
.nest(routes::GATEWAY, gateway::routes(config.gateway))
.nest(routes::MIXNODE, mixnode::routes(config.mixnide))
.nest(routes::MIXNODE, mixnode::routes(config.mixnode))
.nest(
routes::NETWORK_REQUESTER,
network_requester::routes(config.network_requester),
)
.route(routes::BUILD_INFO, get(build_info))
.route(routes::ROLES, get(roles))
.route(
routes::BUILD_INFO,
get({
let build_information = config.build_information;
move |query| build_info(build_information, query)
}),
)
.route(
routes::ROLES,
get({
let node_roles = config.roles;
move |query| roles(node_roles, query)
}),
)
.merge(openapi::route())
}
@@ -2,13 +2,27 @@
// SPDX-License-Identifier: Apache-2.0
use crate::http::state::AppState;
use axum::http::StatusCode;
use axum::routing::get;
use axum::Router;
#[derive(Debug, Clone, Default)]
pub struct Config {}
pub mod root;
pub mod types;
pub(crate) fn routes(_config: Config) -> Router<AppState> {
Router::new().route("/", get(|| async { StatusCode::NOT_IMPLEMENTED }))
pub(crate) mod routes {
pub(crate) const ROOT: &str = "/";
}
#[derive(Debug, Clone, Default)]
pub struct Config {
pub details: Option<types::NetworkRequester>,
}
pub(crate) fn routes(config: Config) -> Router<AppState> {
Router::new().route(
routes::ROOT,
get({
let network_requester_details = config.details;
move |query| root::root_network_requester(network_requester_details, query)
}),
)
}
@@ -0,0 +1,33 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::api::v1::network_requester::types::NetworkRequester;
use crate::http::router::api::{FormattedResponse, OutputParams};
use axum::extract::Query;
use axum::http::StatusCode;
/// Returns root network requester information
#[utoipa::path(
get,
path = "",
context_path = "/api/v1/network-requester",
tag = "Network Requester",
responses(
(status = 501, description = "the endpoint hasn't been implemented yet"),
(status = 200, content(
("application/json" = NetworkRequester),
("application/yaml" = NetworkRequester)
))
),
params(OutputParams)
)]
pub(crate) async fn root_network_requester(
details: Option<NetworkRequester>,
Query(output): Query<OutputParams>,
) -> Result<NetworkRequesterResponse, StatusCode> {
let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(details))
}
pub type NetworkRequesterResponse = FormattedResponse<NetworkRequester>;
@@ -0,0 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::Serialize;
use utoipa::ToSchema;
#[derive(Serialize, Debug, Clone, ToSchema)]
pub struct NetworkRequester {
/// Base58 encoded ed25519 EdDSA public key of the network requester.
pub encoded_identity_key: String,
/// Base58-encoded x25519 public key used for performing key exchange with remote clients.
pub encoded_sphinx_key: String,
}
+15 -15
View File
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::roles::NodeRoles;
use crate::http::router::api;
use crate::http::state::AppState;
use axum::Router;
@@ -8,28 +9,27 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
#[derive(OpenApi)]
#[openapi(
info(title = "NymNode API"),
paths(api::v1::build_info::build_info, api::v1::gateway::root::root_gateway,),
components(schemas(api::Output, api::OutputParams, BinaryBuildInformationOwned))
)]
/*
#[derive(OpenApi)]
#[openapi(
info(title = "NymNode API"),
paths(
api::v1::build_info::build_info,
api::v1::roles::roles,
api::v1::gateway::root::root_gateway,
api::v1::mixnode::root::root_mixnode,
api::v1::network_requester::root::root_network_requester,
),
components(
schemas(
api::Output,
api::OutputParams,
BinaryBuildInformationOwned
)
)
components(schemas(
api::Output,
api::OutputParams,
BinaryBuildInformationOwned,
NodeRoles,
api::v1::gateway::types::Gateway,
api::v1::mixnode::types::Mixnode,
api::v1::network_requester::types::NetworkRequester,
))
)]
*/
pub(crate) struct ApiDoc;
pub(crate) fn route() -> Router<AppState> {
+30 -15
View File
@@ -1,23 +1,38 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::router::api::Output;
use crate::http::router::api::{FormattedResponse, OutputParams};
use axum::extract::Query;
use axum::response::IntoResponse;
use serde::Serialize;
use utoipa::ToSchema;
pub enum NodeRole {
Mixnode {
//
},
Gateway {
//
},
NetworkRequester {
//
},
#[derive(Clone, Debug, Copy, ToSchema, Serialize)]
pub struct NodeRoles {
pub mixnode_enabled: bool,
pub gateway_enabled: bool,
pub network_requester_enabled: bool,
}
pub async fn roles(output: Query<Option<Output>>) -> impl IntoResponse {
let output = output.unwrap_or_default();
todo!()
/// Returns roles supported by this node
#[utoipa::path(
get,
path = "/roles",
context_path = "/api/v1",
tag = "Base",
responses(
(status = 200, content(
("application/json" = NodeRoles),
("application/yaml" = NodeRoles)
))
),
params(OutputParams)
)]
pub(crate) async fn roles(
node_roles: NodeRoles,
Query(output): Query<OutputParams>,
) -> RolesResponse {
let output = output.output.unwrap_or_default();
output.to_response(node_roles)
}
pub type RolesResponse = FormattedResponse<NodeRoles>;
+1 -1
View File
@@ -37,7 +37,7 @@ pub struct NymNodeRouter {
impl NymNodeRouter {
pub fn new(config: Config) -> NymNodeRouter {
let state = AppState::new(config.api.v1_config.build_information.clone());
let state = AppState::new();
NymNodeRouter {
inner: Router::new()
+5 -7
View File
@@ -1,7 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use std::ops::Deref;
use std::sync::Arc;
@@ -10,10 +9,12 @@ pub(crate) struct AppState {
inner: Arc<AppStateInner>,
}
// TODO: https://docs.rs/axum/latest/axum/extract/struct.State.html#substates
impl AppState {
pub fn new(build_information: BinaryBuildInformationOwned) -> Self {
pub fn new() -> Self {
AppState {
inner: Arc::new(AppStateInner { build_information }),
inner: Arc::new(AppStateInner {}),
}
}
}
@@ -28,7 +29,4 @@ impl Deref for AppState {
}
#[derive(Debug)]
pub(crate) struct AppStateInner {
// TODO: split it based on routes?
pub(crate) build_information: BinaryBuildInformationOwned,
}
pub(crate) struct AppStateInner {}