Skip to main content

kora_rpc/openapi/
docs.rs

1use kora_lib::{config::ValidationConfig, transaction::TokenPriceInfo};
2use std::path::PathBuf;
3use utoipa::{
4    openapi::{
5        path::OperationBuilder, request_body::RequestBodyBuilder, ContentBuilder, PathItem,
6        PathItemType, Required, ResponseBuilder, ResponsesBuilder, ServerBuilder,
7    },
8    OpenApi,
9};
10
11use crate::{
12    method::{
13        get_blockhash::GetBlockhashResponse,
14        get_config::GetConfigResponse,
15        get_supported_tokens::GetSupportedTokensResponse,
16        sign_and_send_transaction::{
17            SignAndSendTransactionRequest, SignAndSendTransactionResponse,
18        },
19        sign_transaction::{SignTransactionRequest, SignTransactionResponse},
20        sign_transaction_if_paid::{SignTransactionIfPaidRequest, SignTransactionIfPaidResponse},
21        transfer_transaction::{TransferTransactionRequest, TransferTransactionResponse},
22    },
23    rpc::KoraRpc,
24};
25
26use super::helper::{build_error_response, request_schema};
27
28const JSON_CONTENT_TYPE: &str = "application/json";
29
30#[derive(OpenApi)]
31#[openapi(
32    info(
33        title = "kora-rpc",
34        version = "0.1.0",
35        description = "RPC server for Kora gasless relayer",
36        license(name = "MIT")
37    ),
38    components(schemas(
39        ValidationConfig,
40        TokenPriceInfo,
41        GetBlockhashResponse,
42        GetConfigResponse,
43        GetSupportedTokensResponse,
44        SignAndSendTransactionRequest,
45        SignAndSendTransactionResponse,
46        SignTransactionRequest,
47        SignTransactionResponse,
48        SignTransactionIfPaidRequest,
49        SignTransactionIfPaidResponse,
50        TransferTransactionRequest,
51        TransferTransactionResponse,
52    ))
53)]
54pub struct ApiDoc;
55
56pub fn update_docs() {
57    let method_specs = KoraRpc::build_docs_spec();
58    let mut combined_doc = ApiDoc::openapi();
59
60    // Get base components
61    let components = combined_doc.components.unwrap_or_default();
62
63    combined_doc.servers =
64        Some(vec![ServerBuilder::new().url("https://api.example.com/v1".to_string()).build()]);
65
66    for spec in method_specs {
67        let content =
68            ContentBuilder::new().schema(request_schema(&spec.name, spec.request.clone())).build();
69
70        let request_body = RequestBodyBuilder::new()
71            .content(JSON_CONTENT_TYPE, content)
72            .required(Some(Required::True))
73            .build();
74
75        let responses = ResponsesBuilder::new()
76            .response(
77                "200",
78                ResponseBuilder::new().description("Successful response").content(
79                    JSON_CONTENT_TYPE,
80                    ContentBuilder::new().schema(spec.response.clone()).build(),
81                ),
82            )
83            .response("429", build_error_response("Exceeded rate limit."))
84            .response("500", build_error_response("Internal server error."))
85            .build();
86
87        let operation =
88            OperationBuilder::new().request_body(Some(request_body)).responses(responses).build();
89
90        let mut path_item = PathItem::new(PathItemType::Post, operation);
91        path_item.summary = Some(spec.name.clone());
92
93        combined_doc.paths.paths.insert(format!("/{}", spec.name), path_item);
94    }
95
96    // Set the components
97    combined_doc.components = Some(components);
98
99    let json = serde_json::to_string_pretty(&combined_doc).unwrap();
100    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/openapi/spec/combined_api.json");
101
102    std::fs::write(&path, json).unwrap();
103
104    let validate_result = std::process::Command::new("swagger-cli")
105        .arg("validate")
106        .arg(path.to_str().unwrap())
107        .output()
108        .unwrap();
109
110    if !validate_result.status.success() {
111        let stderr = String::from_utf8_lossy(&validate_result.stderr);
112        panic!("Failed to validate OpenAPI schema: {}", stderr);
113    }
114}