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 error;
|
||||
pub mod middleware;
|
||||
#[cfg(feature = "openapi")]
|
||||
pub mod openapi;
|
||||
pub mod response;
|
||||
pub mod routes;
|
||||
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 serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "openapi")]
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Standard API response wrapper.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
|
@ -43,6 +45,7 @@ pub struct ResponseMeta {
|
|||
|
||||
/// Pagination metadata.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||
pub struct PaginationMeta {
|
||||
/// Total number of items
|
||||
pub total: u64,
|
||||
|
|
@ -247,6 +250,7 @@ impl Default for SortOrder {
|
|||
|
||||
/// Health check response.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||
pub struct HealthResponse {
|
||||
/// Overall health status
|
||||
pub status: HealthStatus,
|
||||
|
|
@ -265,6 +269,7 @@ pub struct HealthResponse {
|
|||
/// Health status enum.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||
pub enum HealthStatus {
|
||||
Healthy,
|
||||
Degraded,
|
||||
|
|
@ -273,6 +278,7 @@ pub enum HealthStatus {
|
|||
|
||||
/// Individual service health.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "openapi", derive(ToSchema))]
|
||||
pub struct ServiceHealth {
|
||||
/// Service name
|
||||
pub name: String,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue