Reempaquetando Swagger UI como un módulo de Rust

created: martes, may. 21, 2024

Todos nuestros servicios necesitan tener un Swagger UI para presentar al usuario su archivo OpenAPI actual. Dado que nuestro backend está completamente construido con Rust, alojar este Swagger UI también se realiza mediante un proceso en Rust.

Así que, para convertir Swagger UI en un ciudadano de primer grado en el ecosistema Rust, decidimos reempaquetar Swagger UI como un módulo de Rust. De esta manera, dependabot puede detectar actualizaciones del proyecto subyacente y traer esas dependencias a nuestros proyectos.

Esto también tiene el beneficio de que cada vez que esta dependencia se actualice, ejecutamos nuestra línea de CI/CD para verificar si la interfaz sigue funcionando correctamente.

Cómo está construido

Swagger UI se empaqueta a través de GitHub. Por lo tanto, determinar la versión actual es simplemente otra llamada a la API de GitHub. Así podemos decidir si es necesario actualizar o no. Si se ha lanzado una nueva versión, descargamos los archivos JS y CSS minificados y actualizamos nuestro repositorio local de Rust con las nuevas dependencias.

Después de actualizar esos archivos, también incrementamos la versión del módulo a la misma versión que el lanzamiento en GitHub. De esta manera, la versión en cargo siempre coincide con la versión en GitHub.

Todo el código requerido para este procedimiento está en la definición de la acción de GitHub.

Cómo usarlo

Usar este crate es bastante sencillo. Los recursos estáticos se exponen como rutas de axum y se pueden fusionar con una definición de ruta existente. Para implementar esas rutas, necesitas especificar un prefijo para las rutas, así como una definición de API. La definición de la API puede ser un archivo YAML en línea o un enlace externo a la definición de la API.

Aquí un ejemplo de cómo usar el crate con una definición OpenAPI en línea.

use axum::Router;
use swagger_ui_dist::{ApiDefinition, OpenApiSource};

#[tokio::main]
async fn main() {
    let api_def = ApiDefinition {
        uri_prefix: "/api",
        api_definition: OpenApiSource::Inline(include_str!("petstore.yaml")),
        title: Some("My Super Duper API"),
    };
    let app = Router::new().merge(swagger_ui_dist::generate_routes(api_def));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    println!("listening on http://localhost:3000/api");
    axum::serve(listener, app).await.unwrap();
}

enlace en crates.io:
https://crates.io/crates/swagger-ui-dist

repositorio en github:
https://github.com/apimeister/swagger-ui-dist-rs/