Skip to content

Commit

Permalink
feat(rust): add a xtask to generate the OpenAPI spec
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed Oct 18, 2024
1 parent 9ec5db0 commit aad4c28
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 126 deletions.
1 change: 1 addition & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 0 additions & 9 deletions rust/agama-server/src/agama-web-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ enum Commands {
/// This command starts the server in the given ports. The secondary port, if enabled, uses SSL.
/// If no certificate is specified, agama-web-server generates a self-signed one.
Serve(ServeArgs),
/// Generates the API documentation in OpenAPI format.
Openapi,
}

/// Manage Agama's HTTP/JSON API.
Expand Down Expand Up @@ -378,16 +376,9 @@ async fn serve_command(args: ServeArgs) -> anyhow::Result<()> {
Ok(())
}

/// Display the API documentation in OpenAPI format.
fn openapi_command() -> anyhow::Result<()> {
println!("{}", web::ApiDoc::build().to_pretty_json()?);
Ok(())
}

async fn run_command(cli: Cli) -> anyhow::Result<()> {
match cli.command {
Commands::Serve(options) => serve_command(options).await,
Commands::Openapi => openapi_command(),
}
}

Expand Down
3 changes: 1 addition & 2 deletions rust/agama-server/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use axum::Router;
mod auth;
pub mod common;
mod config;
mod docs;
pub mod docs;
mod event;
mod http;
mod service;
Expand All @@ -50,7 +50,6 @@ mod ws;

use agama_lib::{connection, error::ServiceError};
pub use config::ServiceConfig;
pub use docs::ApiDoc;
pub use event::{Event, EventsReceiver, EventsSender};
pub use service::MainServiceBuilder;
use std::path::Path;
Expand Down
51 changes: 17 additions & 34 deletions rust/agama-server/src/web/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use utoipa::openapi::{ComponentsBuilder, InfoBuilder, PathsBuilder};
use utoipa::openapi::{Components, InfoBuilder, OpenApiBuilder, Paths};

mod network;
pub use network::NetworkApiDocBuilder;
Expand All @@ -34,45 +34,28 @@ mod manager;
pub use manager::ManagerApiDocBuilder;
mod users;
pub use users::UsersApiDocBuilder;
mod misc;
pub use misc::MiscApiDocBuilder;

pub struct ApiDoc;
pub trait ApiDocBuilder {
fn title(&self) -> String {
"Agama HTTP API".to_string()
}

impl ApiDoc {
pub fn build() -> utoipa::openapi::OpenApi {
let info = InfoBuilder::new()
.title("Agama HTTP API")
.version("0.1.0")
.build();
fn paths(&self) -> Paths;

let paths = PathsBuilder::new()
.path_from::<super::http::__path_ping>()
.build();
fn components(&self) -> Components;

let components = ComponentsBuilder::new()
.schema_from::<super::http::PingResponse>()
fn build(&self) -> utoipa::openapi::OpenApi {
let info = InfoBuilder::new()
.title(self.title())
.version("0.1.0")
.build();

let mut openapi = utoipa::openapi::OpenApiBuilder::new()
OpenApiBuilder::new()
.info(info)
.paths(paths)
.components(Some(components))
.build();

let l10n = L10nApiDocBuilder::build();
let manager = ManagerApiDocBuilder::build();
let network = NetworkApiDocBuilder::build();
let questions = QuestionsApiDocBuilder::build();
let software = SoftwareApiDocBuilder::build();
let storage = StorageApiDocBuilder::build();
let users = UsersApiDocBuilder::build();

openapi.merge(l10n);
openapi.merge(manager);
openapi.merge(network);
openapi.merge(questions);
openapi.merge(software);
openapi.merge(storage);
openapi.merge(users);
openapi
.paths(self.paths())
.components(Some(self.components()))
.build()
}
}
25 changes: 14 additions & 11 deletions rust/agama-server/src/web/docs/l10n.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
use utoipa::openapi::{ComponentsBuilder, OpenApi, OpenApiBuilder, PathsBuilder};
use utoipa::openapi::{Components, ComponentsBuilder, Paths, PathsBuilder};

use super::ApiDocBuilder;

pub struct L10nApiDocBuilder;

impl L10nApiDocBuilder {
pub fn build() -> OpenApi {
let paths = PathsBuilder::new()
impl ApiDocBuilder for L10nApiDocBuilder {
fn title(&self) -> String {
"Localization HTTP API".to_string()
}

fn paths(&self) -> Paths {
PathsBuilder::new()
.path_from::<crate::l10n::web::__path_get_config>()
.path_from::<crate::l10n::web::__path_keymaps>()
.path_from::<crate::l10n::web::__path_locales>()
.path_from::<crate::l10n::web::__path_set_config>()
.path_from::<crate::l10n::web::__path_timezones>()
.build();
.build()
}

let components = ComponentsBuilder::new()
fn components(&self) -> Components {
ComponentsBuilder::new()
.schema_from::<agama_lib::localization::model::LocaleConfig>()
.schema_from::<agama_locale_data::KeymapId>()
.schema_from::<agama_locale_data::LocaleId>()
.schema_from::<crate::l10n::Keymap>()
.schema_from::<crate::l10n::LocaleEntry>()
.schema_from::<crate::l10n::TimezoneEntry>()
.build();

OpenApiBuilder::new()
.paths(paths)
.components(Some(components))
.build()
}
}
25 changes: 14 additions & 11 deletions rust/agama-server/src/web/docs/manager.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
use utoipa::openapi::{ComponentsBuilder, OpenApi, OpenApiBuilder, PathsBuilder};
use utoipa::openapi::{ComponentsBuilder, PathsBuilder};

use super::ApiDocBuilder;

pub struct ManagerApiDocBuilder;

impl ManagerApiDocBuilder {
pub fn build() -> OpenApi {
let paths = PathsBuilder::new()
impl ApiDocBuilder for ManagerApiDocBuilder {
fn title(&self) -> String {
"Manager HTTP API".to_string()
}

fn paths(&self) -> utoipa::openapi::Paths {
PathsBuilder::new()
.path_from::<crate::manager::web::__path_finish_action>()
.path_from::<crate::manager::web::__path_install_action>()
.path_from::<crate::manager::web::__path_installer_status>()
.path_from::<crate::manager::web::__path_probe_action>()
.build();
.build()
}

let components = ComponentsBuilder::new()
fn components(&self) -> utoipa::openapi::Components {
ComponentsBuilder::new()
.schema_from::<agama_lib::manager::InstallationPhase>()
.schema_from::<crate::manager::web::InstallerStatus>()
.build();

OpenApiBuilder::new()
.paths(paths)
.components(Some(components))
.build()
}
}
23 changes: 23 additions & 0 deletions rust/agama-server/src/web/docs/misc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use utoipa::openapi::{Components, ComponentsBuilder, Paths, PathsBuilder};

use super::ApiDocBuilder;

pub struct MiscApiDocBuilder;

impl ApiDocBuilder for MiscApiDocBuilder {
fn title(&self) -> String {
"Miscelaneous HTTP API".to_string()
}

fn paths(&self) -> Paths {
PathsBuilder::new()
.path_from::<crate::web::http::__path_ping>()
.build()
}

fn components(&self) -> Components {
ComponentsBuilder::new()
.schema_from::<crate::web::http::PingResponse>()
.build()
}
}
25 changes: 14 additions & 11 deletions rust/agama-server/src/web/docs/network.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use serde_json::json;
use utoipa::openapi::{ComponentsBuilder, ObjectBuilder, OpenApiBuilder, PathsBuilder};
use utoipa::openapi::{Components, ComponentsBuilder, ObjectBuilder, Paths, PathsBuilder};

use super::ApiDocBuilder;

pub struct NetworkApiDocBuilder;

impl NetworkApiDocBuilder {
pub fn build() -> utoipa::openapi::OpenApi {
let paths = PathsBuilder::new()
impl ApiDocBuilder for NetworkApiDocBuilder {
fn title(&self) -> String {
"Network HTTP API".to_string()
}

fn paths(&self) -> Paths {
PathsBuilder::new()
.path_from::<crate::network::web::__path_add_connection>()
.path_from::<crate::network::web::__path_apply>()
.path_from::<crate::network::web::__path_connect>()
Expand All @@ -15,9 +21,11 @@ impl NetworkApiDocBuilder {
.path_from::<crate::network::web::__path_disconnect>()
.path_from::<crate::network::web::__path_update_connection>()
.path_from::<crate::network::web::__path_apply>()
.build();
.build()
}

let components = ComponentsBuilder::new()
fn components(&self) -> Components {
ComponentsBuilder::new()
.schema_from::<agama_lib::network::settings::BondSettings>()
.schema_from::<agama_lib::network::settings::MatchSettings>()
.schema_from::<agama_lib::network::settings::NetworkConnection>()
Expand Down Expand Up @@ -81,11 +89,6 @@ impl NetworkApiDocBuilder {
.description(Some("MAC address in EUI-48 format".to_string()))
.build(),
)
.build();

OpenApiBuilder::new()
.paths(paths)
.components(Some(components))
.build()
}
}
24 changes: 13 additions & 11 deletions rust/agama-server/src/web/docs/questions.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
use utoipa::openapi::{ComponentsBuilder, OpenApi, OpenApiBuilder, PathsBuilder};
use utoipa::openapi::{Components, ComponentsBuilder, Paths, PathsBuilder};

use super::ApiDocBuilder;

pub struct QuestionsApiDocBuilder;

impl QuestionsApiDocBuilder {
pub fn build() -> OpenApi {
let paths = PathsBuilder::new()
impl ApiDocBuilder for QuestionsApiDocBuilder {
fn title(&self) -> String {
"Questions HTTP API".to_string()
}
fn paths(&self) -> Paths {
PathsBuilder::new()
.path_from::<crate::questions::web::__path_answer_question>()
.path_from::<crate::questions::web::__path_create_question>()
.path_from::<crate::questions::web::__path_delete_question>()
.path_from::<crate::questions::web::__path_get_answer>()
.path_from::<crate::questions::web::__path_list_questions>()
.build();
.build()
}

let components = ComponentsBuilder::new()
fn components(&self) -> Components {
ComponentsBuilder::new()
.schema_from::<agama_lib::questions::model::Answer>()
.schema_from::<agama_lib::questions::model::GenericAnswer>()
.schema_from::<agama_lib::questions::model::GenericQuestion>()
.schema_from::<agama_lib::questions::model::PasswordAnswer>()
.schema_from::<agama_lib::questions::model::Question>()
.schema_from::<agama_lib::questions::model::QuestionWithPassword>()
.build();

OpenApiBuilder::new()
.paths(paths)
.components(Some(components))
.build()
}
}
25 changes: 14 additions & 11 deletions rust/agama-server/src/web/docs/software.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
use utoipa::openapi::{ComponentsBuilder, OpenApi, OpenApiBuilder, PathsBuilder};
use utoipa::openapi::{Components, ComponentsBuilder, Paths, PathsBuilder};

use super::ApiDocBuilder;

pub struct SoftwareApiDocBuilder;

impl SoftwareApiDocBuilder {
pub fn build() -> OpenApi {
let paths = PathsBuilder::new()
impl ApiDocBuilder for SoftwareApiDocBuilder {
fn title(&self) -> String {
"Software HTTP API".to_string()
}

fn paths(&self) -> Paths {
PathsBuilder::new()
.path_from::<crate::software::web::__path_get_config>()
.path_from::<crate::software::web::__path_patterns>()
.path_from::<crate::software::web::__path_probe>()
.path_from::<crate::software::web::__path_products>()
.path_from::<crate::software::web::__path_proposal>()
.path_from::<crate::software::web::__path_set_config>()
.build();
.build()
}

let components = ComponentsBuilder::new()
fn components(&self) -> Components {
ComponentsBuilder::new()
.schema_from::<agama_lib::product::Product>()
.schema_from::<agama_lib::product::RegistrationRequirement>()
.schema_from::<agama_lib::software::Pattern>()
Expand All @@ -22,11 +30,6 @@ impl SoftwareApiDocBuilder {
.schema_from::<agama_lib::software::SelectedBy>()
.schema_from::<agama_lib::software::model::SoftwareConfig>()
.schema_from::<crate::software::web::SoftwareProposal>()
.build();

OpenApiBuilder::new()
.paths(paths)
.components(Some(components))
.build()
}
}
Loading

0 comments on commit aad4c28

Please sign in to comment.