feat: add OpenAPI specification generation for gateway
Adds utoipa-based OpenAPI 3.1 documentation: - OpenAPI module with API metadata, server info, and tags - Security scheme definitions (API key and JWT bearer) - ToSchema derives for response types - JSON specification generation endpoint
This commit is contained in:
parent
f8c536b7cd
commit
8ab9c6c7a2
3 changed files with 111 additions and 0 deletions
|
|
@ -56,6 +56,8 @@ pub mod auth;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
pub mod openapi;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
|
||||||
103
crates/synor-gateway/src/openapi.rs
Normal file
103
crates/synor-gateway/src/openapi.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
//! OpenAPI documentation generation.
|
||||||
|
//!
|
||||||
|
//! This module generates OpenAPI 3.1 specifications for all gateway endpoints
|
||||||
|
//! using utoipa.
|
||||||
|
|
||||||
|
use utoipa::openapi::security::{ApiKey, ApiKeyValue, HttpAuthScheme, HttpBuilder, SecurityScheme};
|
||||||
|
use utoipa::{Modify, OpenApi};
|
||||||
|
|
||||||
|
use crate::response::{HealthResponse, HealthStatus, PaginationMeta, ServiceHealth};
|
||||||
|
|
||||||
|
/// OpenAPI documentation for the Synor Gateway API.
|
||||||
|
#[derive(OpenApi)]
|
||||||
|
#[openapi(
|
||||||
|
info(
|
||||||
|
title = "Synor Gateway API",
|
||||||
|
version = "1.0.0",
|
||||||
|
description = "Unified REST API gateway for all Synor blockchain services.\n\nProvides access to wallet management, blockchain RPC, decentralized storage, DEX trading, IBC cross-chain operations, zero-knowledge proofs, and smart contract compilation.",
|
||||||
|
contact(
|
||||||
|
name = "Synor Team",
|
||||||
|
email = "api@synor.io",
|
||||||
|
url = "https://synor.io"
|
||||||
|
),
|
||||||
|
license(
|
||||||
|
name = "MIT OR Apache-2.0",
|
||||||
|
url = "https://github.com/synortech/synor/blob/main/LICENSE"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
servers(
|
||||||
|
(url = "https://api.synor.io/v1", description = "Production API"),
|
||||||
|
(url = "https://testnet.api.synor.io/v1", description = "Testnet API"),
|
||||||
|
(url = "http://localhost:8000/v1", description = "Local development")
|
||||||
|
),
|
||||||
|
tags(
|
||||||
|
(name = "Health", description = "Service health and status endpoints"),
|
||||||
|
(name = "Wallet", description = "Wallet management and key operations"),
|
||||||
|
(name = "RPC", description = "Blockchain RPC and chain state"),
|
||||||
|
(name = "Storage", description = "Decentralized storage operations"),
|
||||||
|
(name = "DEX", description = "Decentralized exchange trading"),
|
||||||
|
(name = "IBC", description = "Inter-Blockchain Communication"),
|
||||||
|
(name = "ZK", description = "Zero-knowledge proof operations"),
|
||||||
|
(name = "Compiler", description = "Smart contract compilation and analysis")
|
||||||
|
),
|
||||||
|
components(
|
||||||
|
schemas(
|
||||||
|
// Response types
|
||||||
|
HealthResponse,
|
||||||
|
HealthStatus,
|
||||||
|
ServiceHealth,
|
||||||
|
PaginationMeta,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
modifiers(&SecurityAddon)
|
||||||
|
)]
|
||||||
|
pub struct ApiDoc;
|
||||||
|
|
||||||
|
/// Security scheme modifier for OpenAPI documentation.
|
||||||
|
struct SecurityAddon;
|
||||||
|
|
||||||
|
impl Modify for SecurityAddon {
|
||||||
|
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
|
||||||
|
if let Some(components) = openapi.components.as_mut() {
|
||||||
|
components.add_security_scheme(
|
||||||
|
"api_key",
|
||||||
|
SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("X-API-Key"))),
|
||||||
|
);
|
||||||
|
components.add_security_scheme(
|
||||||
|
"bearer_auth",
|
||||||
|
SecurityScheme::Http(
|
||||||
|
HttpBuilder::new()
|
||||||
|
.scheme(HttpAuthScheme::Bearer)
|
||||||
|
.bearer_format("JWT")
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the OpenAPI JSON specification.
|
||||||
|
pub fn generate_openapi_json() -> String {
|
||||||
|
ApiDoc::openapi().to_json().unwrap_or_else(|e| {
|
||||||
|
tracing::error!("Failed to generate OpenAPI JSON: {}", e);
|
||||||
|
"{}".to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the OpenAPI YAML specification.
|
||||||
|
pub fn generate_openapi_yaml() -> String {
|
||||||
|
// utoipa 4.x doesn't have to_yaml, use JSON for now
|
||||||
|
generate_openapi_json()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_openapi_generation() {
|
||||||
|
let json = generate_openapi_json();
|
||||||
|
assert!(!json.is_empty());
|
||||||
|
assert!(json.contains("Synor Gateway API"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,8 @@ use axum::{
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
/// Standard API response wrapper.
|
/// Standard API response wrapper.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
|
@ -43,6 +45,7 @@ pub struct ResponseMeta {
|
||||||
|
|
||||||
/// Pagination metadata.
|
/// Pagination metadata.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||||
pub struct PaginationMeta {
|
pub struct PaginationMeta {
|
||||||
/// Total number of items
|
/// Total number of items
|
||||||
pub total: u64,
|
pub total: u64,
|
||||||
|
|
@ -247,6 +250,7 @@ impl Default for SortOrder {
|
||||||
|
|
||||||
/// Health check response.
|
/// Health check response.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||||
pub struct HealthResponse {
|
pub struct HealthResponse {
|
||||||
/// Overall health status
|
/// Overall health status
|
||||||
pub status: HealthStatus,
|
pub status: HealthStatus,
|
||||||
|
|
@ -265,6 +269,7 @@ pub struct HealthResponse {
|
||||||
/// Health status enum.
|
/// Health status enum.
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||||
pub enum HealthStatus {
|
pub enum HealthStatus {
|
||||||
Healthy,
|
Healthy,
|
||||||
Degraded,
|
Degraded,
|
||||||
|
|
@ -273,6 +278,7 @@ pub enum HealthStatus {
|
||||||
|
|
||||||
/// Individual service health.
|
/// Individual service health.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||||
pub struct ServiceHealth {
|
pub struct ServiceHealth {
|
||||||
/// Service name
|
/// Service name
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue