diff --git a/rust/agama-lib/src/network/settings.rs b/rust/agama-lib/src/network/settings.rs index d5d4a3584e..3e730f7e22 100644 --- a/rust/agama-lib/src/network/settings.rs +++ b/rust/agama-lib/src/network/settings.rs @@ -74,7 +74,7 @@ pub struct NetworkDevice { pub state: DeviceState, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, utoipa::ToSchema)] pub struct NetworkConnection { pub id: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/rust/agama-server/src/l10n/web.rs b/rust/agama-server/src/l10n/web.rs index 0288ada8b9..4ec36694cb 100644 --- a/rust/agama-server/src/l10n/web.rs +++ b/rust/agama-server/src/l10n/web.rs @@ -76,18 +76,28 @@ pub struct LocaleConfig { ui_keymap: Option, } -#[utoipa::path(get, path = "/l10n/timezones", responses( - (status = 200, description = "List of known timezones") -))] +#[utoipa::path( + get, + path = "/timezones", + context_path = "/api/l10n", + responses( + (status = 200, description = "List of known timezones", body = Vec) + ) +)] async fn timezones(State(state): State>) -> Json> { let data = state.locale.read().await; let timezones = data.timezones_db.entries().to_vec(); Json(timezones) } -#[utoipa::path(get, path = "/l10n/keymaps", responses( - (status = 200, description = "List of known keymaps", body = Vec) -))] +#[utoipa::path( + get, + path = "/keymaps", + context_path = "/api/l10n", + responses( + (status = 200, description = "List of known keymaps", body = Vec) + ) +)] async fn keymaps(State(state): State>) -> Json> { let data = state.locale.read().await; let keymaps = data.keymaps_db.entries().to_vec(); @@ -96,9 +106,15 @@ async fn keymaps(State(state): State>) -> Json> { // TODO: update all or nothing // TODO: send only the attributes that have changed -#[utoipa::path(patch, path = "/l10n/config", responses( - (status = 204, description = "Set the locale configuration", body = LocaleConfig) -))] +#[utoipa::path( + patch, + path = "/config", + context_path = "/api/l10n", + operation_id = "set_l10n_config", + responses( + (status = 204, description = "Set the locale configuration", body = LocaleConfig) + ) +)] async fn set_config( State(state): State>, Json(value): Json, @@ -148,9 +164,15 @@ async fn set_config( Ok(StatusCode::NO_CONTENT) } -#[utoipa::path(get, path = "/l10n/config", responses( - (status = 200, description = "Localization configuration", body = LocaleConfig) -))] +#[utoipa::path( + get, + path = "/config", + context_path = "/api/l10n", + operation_id = "get_l10n_config", + responses( + (status = 200, description = "Localization configuration", body = LocaleConfig) + ) +)] async fn get_config(State(state): State>) -> Json { let data = state.locale.read().await; Json(LocaleConfig { diff --git a/rust/agama-server/src/manager/web.rs b/rust/agama-server/src/manager/web.rs index 404bb72f84..475f97dd50 100644 --- a/rust/agama-server/src/manager/web.rs +++ b/rust/agama-server/src/manager/web.rs @@ -96,36 +96,56 @@ pub async fn manager_service(dbus: zbus::Connection) -> Result>) -> Result<(), Error> { state.manager.probe().await?; Ok(()) } /// Starts the probing process. -#[utoipa::path(get, path = "/api/manager/install", responses( - (status = 200, description = "The installation process was started.") -))] +#[utoipa::path( + get, + path = "/install", + context_path = "/api/manager", + responses( + (status = 200, description = "The installation process was started.") + ) +)] async fn install_action(State(state): State>) -> Result<(), Error> { state.manager.install().await?; Ok(()) } /// Executes the post installation tasks (e.g., rebooting the system). -#[utoipa::path(get, path = "/api/manager/install", responses( - (status = 200, description = "The installation tasks are executed.") -))] +#[utoipa::path( + get, + path = "/install", + context_path = "/api/manager", + responses( + (status = 200, description = "The installation tasks are executed.") + ) +)] async fn finish_action(State(state): State>) -> Result<(), Error> { state.manager.finish().await?; Ok(()) } /// Returns the manager status. -#[utoipa::path(get, path = "/api/manager/installer", responses( - (status = 200, description = "Installation status.", body = ManagerStatus) -))] +#[utoipa::path( + get, + path = "/installer", + context_path = "/api/manager", + responses( + (status = 200, description = "Installation status.", body = InstallerStatus) + ) +)] async fn installer_status( State(state): State>, ) -> Result, Error> { diff --git a/rust/agama-server/src/network/model.rs b/rust/agama-server/src/network/model.rs index 8e28964607..cba18b0612 100644 --- a/rust/agama-server/src/network/model.rs +++ b/rust/agama-server/src/network/model.rs @@ -38,7 +38,7 @@ impl Default for StateConfig { } } -#[derive(Default, Clone, Debug, utoipa::ToSchema)] +#[derive(Default, Clone, Debug)] pub struct NetworkState { pub general_state: GeneralState, pub access_points: Vec, diff --git a/rust/agama-server/src/network/web.rs b/rust/agama-server/src/network/web.rs index a580225ba6..19607cc222 100644 --- a/rust/agama-server/src/network/web.rs +++ b/rust/agama-server/src/network/web.rs @@ -110,9 +110,14 @@ pub async fn network_service( .with_state(state)) } -#[utoipa::path(get, path = "/network/state", responses( - (status = 200, description = "Get general network config", body = GenereralState) -))] +#[utoipa::path( + get, + path = "/state", + context_path = "/api/network", + responses( + (status = 200, description = "Get general network config", body = GenereralState) + ) +)] async fn general_state( State(state): State, ) -> Result, NetworkError> { @@ -120,9 +125,14 @@ async fn general_state( Ok(Json(general_state)) } -#[utoipa::path(put, path = "/network/state", responses( - (status = 200, description = "Update general network config", body = GenereralState) -))] +#[utoipa::path( + put, + path = "/state", + context_path = "/api/network", + responses( + (status = 200, description = "Update general network config", body = GenereralState) + ) +)] async fn update_general_state( State(state): State, Json(value): Json, @@ -132,9 +142,14 @@ async fn update_general_state( Ok(Json(state)) } -#[utoipa::path(get, path = "/network/wifi", responses( - (status = 200, description = "List of wireless networks", body = Vec) -))] +#[utoipa::path( + get, + path = "/wifi", + context_path = "/api/network", + responses( + (status = 200, description = "List of wireless networks", body = Vec) + ) +)] async fn wifi_networks( State(state): State, ) -> Result>, NetworkError> { @@ -151,36 +166,28 @@ async fn wifi_networks( Ok(Json(networks)) } -#[utoipa::path(get, path = "/network/devices", responses( - (status = 200, description = "List of devices", body = Vec) -))] +#[utoipa::path( + get, + path = "/devices", + context_path = "/api/network", + responses( + (status = 200, description = "List of devices", body = Vec) + ) +)] async fn devices( State(state): State, ) -> Result>, NetworkError> { Ok(Json(state.network.get_devices().await?)) } -#[utoipa::path(get, path = "/network/connections/:id", responses( - (status = 200, description = "Get connection given by its ID", body = NetworkConnection) -))] -async fn connection( - State(state): State, - Path(id): Path, -) -> Result, NetworkError> { - let conn = state - .network - .get_connection(&id) - .await? - .ok_or_else(|| NetworkError::UnknownConnection(id.clone()))?; - - let conn = NetworkConnection::try_from(conn)?; - - Ok(Json(conn)) -} - -#[utoipa::path(get, path = "/network/connections", responses( - (status = 200, description = "List of known connections", body = Vec) -))] +#[utoipa::path( + get, + path = "/connections", + context_path = "/api/network", + responses( + (status = 200, description = "List of known connections", body = Vec) + ) +)] async fn connections( State(state): State, ) -> Result>, NetworkError> { @@ -193,9 +200,14 @@ async fn connections( Ok(Json(connections)) } -#[utoipa::path(post, path = "/network/connections", responses( - (status = 200, description = "Add a new connection", body = Connection) -))] +#[utoipa::path( + post, + path = "/connections", + context_path = "/api/network", + responses( + (status = 200, description = "Add a new connection", body = Connection) + ) +)] async fn add_connection( State(state): State, Json(conn): Json, @@ -210,9 +222,36 @@ async fn add_connection( } } -#[utoipa::path(delete, path = "/network/connections/:id", responses( - (status = 200, description = "Delete connection", body = Connection) -))] +#[utoipa::path( + get, + path = "/network/connections/:id", + responses( + (status = 200, description = "Get connection given by its ID", body = NetworkConnection) + ) +)] +async fn connection( + State(state): State, + Path(id): Path, +) -> Result, NetworkError> { + let conn = state + .network + .get_connection(&id) + .await? + .ok_or_else(|| NetworkError::UnknownConnection(id.clone()))?; + + let conn = NetworkConnection::try_from(conn)?; + + Ok(Json(conn)) +} + +#[utoipa::path( + delete, + path = "/connections/:id", + context_path = "/api/network", + responses( + (status = 200, description = "Delete connection", body = Connection) + ) +)] async fn delete_connection( State(state): State, Path(id): Path, @@ -224,9 +263,14 @@ async fn delete_connection( } } -#[utoipa::path(put, path = "/network/connections/:id", responses( - (status = 204, description = "Update connection", body = Connection) -))] +#[utoipa::path( + put, + path = "/connections/:id", + context_path = "/api/network", + responses( + (status = 204, description = "Update connection", body = Connection) + ) +)] async fn update_connection( State(state): State, Path(id): Path, @@ -249,9 +293,14 @@ async fn update_connection( Ok(StatusCode::NO_CONTENT) } -#[utoipa::path(get, path = "/network/connections/:id/connect", responses( - (status = 204, description = "Connect to the given connection", body = String) -))] +#[utoipa::path( + get, + path = "/connections/:id/connect", + context_path = "/api/network", + responses( + (status = 204, description = "Connect to the given connection", body = String) + ) +)] async fn connect( State(state): State, Path(id): Path, @@ -270,9 +319,14 @@ async fn connect( Ok(StatusCode::NO_CONTENT) } -#[utoipa::path(get, path = "/network/connections/:id/disconnect", responses( - (status = 204, description = "Connect to the given connection", body = String) -))] +#[utoipa::path( + get, + path = "/connections/:id/disconnect", + context_path = "/api/network", + responses( + (status = 204, description = "Connect to the given connection", body = String) + ) +)] async fn disconnect( State(state): State, Path(id): Path, @@ -291,9 +345,14 @@ async fn disconnect( Ok(StatusCode::NO_CONTENT) } -#[utoipa::path(put, path = "/network/system/apply", responses( - (status = 204, description = "Apply configuration") -))] +#[utoipa::path( + put, + path = "/system/apply", + context_path = "/api/network", + responses( + (status = 204, description = "Apply configuration") + ) +)] async fn apply( State(state): State, ) -> Result { diff --git a/rust/agama-server/src/software/web.rs b/rust/agama-server/src/software/web.rs index 7181fbf370..b1a15c4c83 100644 --- a/rust/agama-server/src/software/web.rs +++ b/rust/agama-server/src/software/web.rs @@ -223,10 +223,15 @@ pub async fn software_service(dbus: zbus::Connection) -> Result), - (status = 400, description = "The D-Bus service could not perform the action") -))] +#[utoipa::path( + get, + path = "/products", + context_path = "/api/software", + responses( + (status = 200, description = "List of known products", body = Vec), + (status = 400, description = "The D-Bus service could not perform the action") + ) +)] async fn products(State(state): State>) -> Result>, Error> { let products = state.product.products().await?; Ok(Json(products)) @@ -248,10 +253,15 @@ pub struct RegistrationInfo { /// returns registration info /// /// * `state`: service state. -#[utoipa::path(get, path = "/software/registration", responses( - (status = 200, description = "registration configuration", body = RegistrationInfo), - (status = 400, description = "The D-Bus service could not perform the action") -))] +#[utoipa::path( + get, + path = "/registration", + context_path = "/api/software", + responses( + (status = 200, description = "registration configuration", body = RegistrationInfo), + (status = 400, description = "The D-Bus service could not perform the action") + ) +)] async fn get_registration( State(state): State>, ) -> Result, Error> { @@ -282,11 +292,16 @@ pub struct FailureDetails { /// Register product /// /// * `state`: service state. -#[utoipa::path(post, path = "/software/registration", responses( - (status = 204, description = "registration successfull"), - (status = 422, description = "Registration failed. Details are in body", body=FailureDetails), - (status = 400, description = "The D-Bus service could not perform the action") -))] +#[utoipa::path( + post, + path = "/registration", + context_path = "/api/software", + responses( + (status = 204, description = "registration successfull"), + (status = 422, description = "Registration failed. Details are in body", body=FailureDetails), + (status = 400, description = "The D-Bus service could not perform the action") + ) +)] async fn register( State(state): State>, Json(config): Json, @@ -306,11 +321,16 @@ async fn register( /// Deregister product /// /// * `state`: service state. -#[utoipa::path(delete, path = "/software/registration", responses( - (status = 200, description = "deregistration successfull"), - (status = 422, description = "De-registration failed. Details are in body", body=FailureDetails), - (status = 400, description = "The D-Bus service could not perform the action") -))] +#[utoipa::path( + delete, + path = "/registration", + context_path = "/api/software", + responses( + (status = 200, description = "deregistration successfull"), + (status = 422, description = "De-registration failed. Details are in body", body=FailureDetails), + (status = 400, description = "The D-Bus service could not perform the action") + ) +)] async fn deregister(State(state): State>) -> Result { let (id, message) = state.product.deregister().await?; let details = FailureDetails { id, message }; @@ -327,10 +347,15 @@ async fn deregister(State(state): State>) -> Result), - (status = 400, description = "The D-Bus service could not perform the action") -))] +#[utoipa::path( + get, + path = "patterns", + context_path = "/api/software", + responses( + (status = 200, description = "List of known software patterns", body = Vec), + (status = 400, description = "The D-Bus service could not perform the action") + ) +)] async fn patterns(State(state): State>) -> Result>, Error> { let patterns = state.software.patterns(true).await?; Ok(Json(patterns)) @@ -340,10 +365,15 @@ async fn patterns(State(state): State>) -> Result>, Json(config): Json, @@ -362,10 +392,16 @@ async fn set_config( /// Returns the software configuration. /// /// * `state` : service state. -#[utoipa::path(get, path = "/software/config", responses( - (status = 200, description = "Software configuration", body = SoftwareConfig), - (status = 400, description = "The D-Bus service could not perform the action") -))] +#[utoipa::path( + get, + path = "/software/config", + context_path = "/api/software", + operation_id = "get_software_config", + responses( + (status = 200, description = "Software configuration", body = SoftwareConfig), + (status = 400, description = "The D-Bus service could not perform the action") + ) +)] async fn get_config(State(state): State>) -> Result, Error> { let product = state.product.product().await?; let product = if product.is_empty() { @@ -402,9 +438,13 @@ pub struct SoftwareProposal { /// /// At this point, only the required space is reported. #[utoipa::path( - get, path = "/software/proposal", responses( + get, + path = "/software/proposal", + context_path = "/api/software", + responses( (status = 200, description = "Software proposal", body = SoftwareProposal) -))] + ) +)] async fn proposal(State(state): State>) -> Result, Error> { let size = state.software.used_disk_space().await?; let patterns = state.software.selected_patterns().await?; @@ -416,11 +456,15 @@ async fn proposal(State(state): State>) -> Result>) -> Result, Error> { state.software.probe().await?; Ok(Json(())) diff --git a/rust/agama-server/src/web/docs.rs b/rust/agama-server/src/web/docs.rs index e6d7d98f66..b89117ae1e 100644 --- a/rust/agama-server/src/web/docs.rs +++ b/rust/agama-server/src/web/docs.rs @@ -8,11 +8,20 @@ use utoipa::OpenApi; crate::l10n::web::locales, crate::l10n::web::set_config, crate::l10n::web::timezones, - crate::network::web::devices, + crate::network::web::add_connection, + crate::network::web::apply, + crate::network::web::connect, crate::network::web::connections, + crate::network::web::delete_connection, + crate::network::web::devices, + crate::network::web::disconnect, + crate::network::web::update_connection, crate::software::web::get_config, crate::software::web::patterns, crate::software::web::set_config, + crate::software::web::proposal, + crate::software::web::probe, + crate::software::web::products, crate::manager::web::probe_action, crate::manager::web::install_action, crate::manager::web::finish_action, @@ -30,6 +39,8 @@ use utoipa::OpenApi; crate::storage::web::iscsi::nodes, crate::storage::web::iscsi::update_node, crate::storage::web::iscsi::delete_node, + crate::storage::web::iscsi::login_node, + crate::storage::web::iscsi::logout_node, crate::storage::web::iscsi::discover ), components( @@ -40,7 +51,6 @@ use utoipa::OpenApi; schemas(crate::l10n::LocaleEntry), schemas(crate::l10n::TimezoneEntry), schemas(crate::l10n::web::LocaleConfig), - schemas(crate::network::model::NetworkState), schemas(crate::network::model::Device), schemas(crate::network::model::Connection), schemas(agama_lib::network::types::DeviceType), @@ -65,6 +75,7 @@ use utoipa::OpenApi; schemas(agama_lib::storage::client::iscsi::ISCSINode), schemas(agama_lib::storage::client::iscsi::ISCSIAuth), schemas(agama_lib::storage::client::iscsi::LoginResult), + schemas(agama_lib::network::settings::NetworkConnection) ) )] pub struct ApiDoc;