diff options
author | rtkay123 <dev@kanjala.com> | 2025-08-12 05:13:32 +0200 |
---|---|---|
committer | rtkay123 <dev@kanjala.com> | 2025-08-12 05:13:32 +0200 |
commit | c5ea875f544824b0c042bf7c0a58b3134f9c0373 (patch) | |
tree | 4913d4ff2b408c7157e33894e40deec570ecce9e /crates/configuration/src/server | |
parent | 9c850d6c4d0ed468709c2eb5340d7b64bbb9aa68 (diff) | |
download | warden-c5ea875f544824b0c042bf7c0a58b3134f9c0373.tar.bz2 warden-c5ea875f544824b0c042bf7c0a58b3134f9c0373.zip |
feat(config): get active routing
Diffstat (limited to 'crates/configuration/src/server')
-rw-r--r-- | crates/configuration/src/server/error.rs | 26 | ||||
-rw-r--r-- | crates/configuration/src/server/http_svc.rs | 63 | ||||
-rw-r--r-- | crates/configuration/src/server/http_svc/routes.rs | 11 | ||||
-rw-r--r-- | crates/configuration/src/server/http_svc/routes/routing.rs | 4 | ||||
-rw-r--r-- | crates/configuration/src/server/http_svc/routes/routing/get_active.rs | 30 | ||||
-rw-r--r-- | crates/configuration/src/server/interceptor.rs | 23 | ||||
-rw-r--r-- | crates/configuration/src/server/version.rs | 35 |
7 files changed, 192 insertions, 0 deletions
diff --git a/crates/configuration/src/server/error.rs b/crates/configuration/src/server/error.rs new file mode 100644 index 0000000..730f99a --- /dev/null +++ b/crates/configuration/src/server/error.rs @@ -0,0 +1,26 @@ +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, +}; + +#[derive(Debug)] +pub struct AppError(anyhow::Error); + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {}", self.0), + ) + .into_response() + } +} + +impl<E> From<E> for AppError +where + E: Into<anyhow::Error>, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} diff --git a/crates/configuration/src/server/http_svc.rs b/crates/configuration/src/server/http_svc.rs new file mode 100644 index 0000000..7b2a258 --- /dev/null +++ b/crates/configuration/src/server/http_svc.rs @@ -0,0 +1,63 @@ +mod routes; + +use axum::{Router, response::IntoResponse}; +use utoipa::OpenApi; +use utoipa_axum::router::OpenApiRouter; +#[cfg(feature = "redoc")] +use utoipa_redoc::Servable; +#[cfg(feature = "scalar")] +use utoipa_scalar::Servable as _; + +use crate::{server::http_svc, state::AppHandle}; + +const TAG_ROUTING: &str = "Routing"; + +#[derive(OpenApi)] +#[openapi( + tags( + (name = TAG_ROUTING, description = "Operations related to routing configuration"), + ) +)] +pub struct ApiDoc; + +/// Get health of the API. +#[utoipa::path( + method(get), + path = "/", + responses( + (status = OK, description = "Success", body = str, content_type = "text/plain") + ) +)] +pub async fn health_check() -> impl IntoResponse { + let name = env!("CARGO_PKG_NAME"); + let ver = env!("CARGO_PKG_VERSION"); + + format!("{name} v{ver} is live") +} + +pub fn build_router(state: AppHandle) -> Router { + let (router, _api) = OpenApiRouter::with_openapi(ApiDoc::openapi()) + .routes(utoipa_axum::routes!(health_check)) + .nest("/api", routes::router(state)) + .split_for_parts(); + + #[cfg(feature = "swagger")] + let router = router.merge( + utoipa_swagger_ui::SwaggerUi::new("/swagger-ui") + .url("/api-docs/swaggerdoc.json", _api.clone()), + ); + + #[cfg(feature = "redoc")] + let router = router.merge(utoipa_redoc::Redoc::with_url("/redoc", _api.clone())); + + #[cfg(feature = "rapidoc")] + let router = router.merge( + utoipa_rapidoc::RapiDoc::with_openapi("/api-docs/rapidoc.json", _api.clone()) + .path("/rapidoc"), + ); + + #[cfg(feature = "scalar")] + let router = router.merge(utoipa_scalar::Scalar::with_url("/scalar", _api)); + + warden_middleware::apply(router) +} diff --git a/crates/configuration/src/server/http_svc/routes.rs b/crates/configuration/src/server/http_svc/routes.rs new file mode 100644 index 0000000..cc065e8 --- /dev/null +++ b/crates/configuration/src/server/http_svc/routes.rs @@ -0,0 +1,11 @@ +mod routing; + +use utoipa_axum::{router::OpenApiRouter, routes}; + +use crate::state::AppHandle; + +pub fn router(store: AppHandle) -> OpenApiRouter { + OpenApiRouter::new() + .routes(routes!(routing::get_active)) + .with_state(store) +} diff --git a/crates/configuration/src/server/http_svc/routes/routing.rs b/crates/configuration/src/server/http_svc/routes/routing.rs new file mode 100644 index 0000000..7c8affe --- /dev/null +++ b/crates/configuration/src/server/http_svc/routes/routing.rs @@ -0,0 +1,4 @@ +//pub mod delete_routing; +pub mod get_active; +//pub mod post_routing; +//pub mod replace_routing; diff --git a/crates/configuration/src/server/http_svc/routes/routing/get_active.rs b/crates/configuration/src/server/http_svc/routes/routing/get_active.rs new file mode 100644 index 0000000..562a1f1 --- /dev/null +++ b/crates/configuration/src/server/http_svc/routes/routing/get_active.rs @@ -0,0 +1,30 @@ +use axum::{extract::State, response::IntoResponse}; +use warden_core::configuration::routing::RoutingConfiguration; + +use crate::{ + server::{error::AppError, http_svc::TAG_ROUTING, version::Version}, + state::AppHandle, +}; + +#[utoipa::path( + get, + responses(( + status = OK, + body = RoutingConfiguration + )), + operation_id = "get_active_routing", // https://github.com/juhaku/utoipa/issues/1170 + path = "/{version}/routing", + params( + ("version" = Version, Path, description = "API version, e.g., v1, v2, v3") + ), + tag = TAG_ROUTING, + ) +] +#[axum::debug_handler] +#[tracing::instrument(skip(state), err(Debug), fields(method = "GET"))] +pub(super) async fn active_routing( + version: Version, + State(state): State<AppHandle>, +) -> Result<impl IntoResponse, AppError> { + Ok(String::default().into_response()) +} diff --git a/crates/configuration/src/server/interceptor.rs b/crates/configuration/src/server/interceptor.rs new file mode 100644 index 0000000..eeb36c2 --- /dev/null +++ b/crates/configuration/src/server/interceptor.rs @@ -0,0 +1,23 @@ +use tonic::{Status, service::Interceptor}; +use tracing::Span; +use warden_stack::{ + opentelemetry::global, tracing::telemetry::tonic::extractor, + tracing_opentelemetry::OpenTelemetrySpanExt, +}; + +#[derive(Clone, Copy)] +pub struct MyInterceptor; + +impl Interceptor for MyInterceptor { + fn call(&mut self, request: tonic::Request<()>) -> Result<tonic::Request<()>, Status> { + let span = Span::current(); + + let cx = global::get_text_map_propagator(|propagator| { + propagator.extract(&extractor::MetadataMap(request.metadata())) + }); + + span.set_parent(cx); + + Ok(request) + } +} diff --git a/crates/configuration/src/server/version.rs b/crates/configuration/src/server/version.rs new file mode 100644 index 0000000..4eb5677 --- /dev/null +++ b/crates/configuration/src/server/version.rs @@ -0,0 +1,35 @@ +use std::collections::HashMap; + +use axum::{ + RequestPartsExt, + extract::{FromRequestParts, Path}, + http::{StatusCode, request::Parts}, + response::{IntoResponse, Response}, +}; +use utoipa::ToSchema; + +#[derive(Debug, ToSchema)] +pub enum Version { + V0, +} + +impl<S> FromRequestParts<S> for Version +where + S: Send + Sync, +{ + type Rejection = Response; + + async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> { + let params: Path<HashMap<String, String>> = + parts.extract().await.map_err(IntoResponse::into_response)?; + + let version = params + .get("version") + .ok_or_else(|| (StatusCode::NOT_FOUND, "version param missing").into_response())?; + + match version.as_str() { + "v0" => Ok(Version::V0), + _ => Err((StatusCode::NOT_FOUND, "unknown version").into_response()), + } + } +} |