Reempaquetando el Swagger UI como un módulo de Rust

created: martes, may. 21, 2024

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

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

Esto también tiene el beneficio de que cada vez que esta dependencia se actualiza, ejecutamos nuestra canalización CI/CD para verificar si la interfaz aún funciona correctamente para nosotros.

How it is build

El Swagger UI se empaqueta a través de GitHub. Así que determinar la versión actual es solo otra llamada contra la API de GitHub. De esta forma podemos decidir si es necesario actualizar o no. Si se ha publicado una nueva versión, descargamos los archivos JS y CSS minimizados 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 la release en GitHub. De esta manera, la versión de cargo siempre coincide con la versión de GitHub.

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

How to use it

Usar este crate es bastante sencillo. Los recursos estáticos se exponen como rutas de axum y pueden combinarse 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();
}

crate.io link: https://crates.io/crates/swagger-ui-dist

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