diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 14fa68ab0..03a37bbb5 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -18,15 +18,42 @@ // To contact SUSE LLC about this file by physical or electronic mail, you may // find current contact information at www.suse.com. -use agama_lib::auth::AuthToken; +use agama_lib::{auth::AuthToken, error::ServiceError}; use clap::Subcommand; use crate::error::CliError; +use agama_lib::base_http_client::BaseHTTPClient; use inquire::Password; -use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; +use std::collections::HashMap; use std::io::{self, IsTerminal}; -const DEFAULT_AUTH_URL: &str = "http://localhost/api/auth"; +/// HTTP client to handle authentication +struct AuthHTTPClient { + api: BaseHTTPClient, +} + +impl AuthHTTPClient { + pub fn load(client: BaseHTTPClient) -> Result { + Ok(Self { api: client }) + } + + /// Query web server for JWT + pub async fn authenticate(&self, password: String) -> anyhow::Result { + let mut auth_body = HashMap::new(); + + auth_body.insert("password", password); + + let response = self + .api + .post::>("/auth", &auth_body) + .await?; + + match response.get("token") { + Some(token) => Ok(token.clone()), + None => Err(anyhow::anyhow!("Failed to get authentication token")), + } + } +} #[derive(Subcommand, Debug)] pub enum AuthCommands { @@ -43,9 +70,11 @@ pub enum AuthCommands { } /// Main entry point called from agama CLI main loop -pub async fn run(subcommand: AuthCommands) -> anyhow::Result<()> { +pub async fn run(client: BaseHTTPClient, subcommand: AuthCommands) -> anyhow::Result<()> { + let auth_client = AuthHTTPClient::load(client)?; + match subcommand { - AuthCommands::Login => login(read_password()?).await, + AuthCommands::Login => login(auth_client, read_password()?).await, AuthCommands::Logout => logout(), AuthCommands::Show => show(), } @@ -57,6 +86,7 @@ pub async fn run(subcommand: AuthCommands) -> anyhow::Result<()> { /// user. fn read_password() -> Result { let stdin = io::stdin(); + let password = if stdin.is_terminal() { ask_password()? } else { @@ -77,40 +107,10 @@ fn ask_password() -> Result { .map_err(CliError::InteractivePassword) } -/// Necessary http request header for authenticate -fn authenticate_headers() -> HeaderMap { - let mut headers = HeaderMap::new(); - - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - - headers -} - -/// Query web server for JWT -async fn get_jwt(url: String, password: String) -> anyhow::Result { - let client = reqwest::Client::new(); - let response = client - .post(url) - .headers(authenticate_headers()) - .body(format!("{{\"password\": \"{}\"}}", password)) - .send() - .await?; - let body = response - .json::>() - .await?; - let value = body.get("token"); - - if let Some(token) = value { - return Ok(token.clone()); - } - - Err(anyhow::anyhow!("Failed to get authentication token")) -} - /// Logs into the installation web server and stores JWT for later use. -async fn login(password: String) -> anyhow::Result<()> { +async fn login(client: AuthHTTPClient, password: String) -> anyhow::Result<()> { // 1) ask web server for JWT - let res = get_jwt(DEFAULT_AUTH_URL.to_string(), password).await?; + let res = client.authenticate(password).await?; let token = AuthToken::new(&res); Ok(token.write_user_token()?) } diff --git a/rust/agama-cli/src/config.rs b/rust/agama-cli/src/config.rs index b77eeb086..d1f11fd10 100644 --- a/rust/agama-cli/src/config.rs +++ b/rust/agama-cli/src/config.rs @@ -25,7 +25,9 @@ use std::{ }; use crate::show_progress; -use agama_lib::{auth::AuthToken, install_settings::InstallSettings, Store as SettingsStore}; +use agama_lib::{ + base_http_client::BaseHTTPClient, install_settings::InstallSettings, Store as SettingsStore, +}; use anyhow::anyhow; use clap::Subcommand; use std::io::Write; @@ -59,14 +61,8 @@ pub enum ConfigCommands { }, } -pub async fn run(subcommand: ConfigCommands) -> anyhow::Result<()> { - let Some(token) = AuthToken::find() else { - println!("You need to login for generating a valid token: agama auth login"); - return Ok(()); - }; - - let client = agama_lib::http_client(token.as_str())?; - let store = SettingsStore::new(client).await?; +pub async fn run(http_client: BaseHTTPClient, subcommand: ConfigCommands) -> anyhow::Result<()> { + let store = SettingsStore::new(http_client).await?; match subcommand { ConfigCommands::Show => { diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 42d265e55..3bec4aa2e 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -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 clap::Parser; +use clap::{Args, Parser}; mod auth; mod commands; @@ -30,22 +30,38 @@ mod progress; mod questions; use crate::error::CliError; +use agama_lib::base_http_client::BaseHTTPClient; use agama_lib::{ error::ServiceError, manager::ManagerClient, progress::ProgressMonitor, transfer::Transfer, }; use auth::run as run_auth_cmd; use commands::Commands; use config::run as run_config_cmd; +use inquire::Confirm; use logs::run as run_logs_cmd; use profile::run as run_profile_cmd; use progress::InstallerProgress; use questions::run as run_questions_cmd; use std::{ + collections::HashMap, process::{ExitCode, Termination}, thread::sleep, time::Duration, }; +/// Agama's CLI global options +#[derive(Args)] +pub struct GlobalOpts { + #[clap(long, default_value = "http://localhost/api")] + /// URI pointing to Agama's remote API. If not provided, default https://localhost/api is + /// used + pub api: String, + + #[clap(long, default_value = "false")] + /// Whether to accept invalid (self-signed, ...) certificates or not + pub insecure: bool, +} + /// Agama's command-line interface /// /// This program allows inspecting or changing Agama's configuration, handling installation @@ -55,6 +71,9 @@ use std::{ #[derive(Parser)] #[command(name = "agama", about, long_about, max_term_width = 100)] pub struct Cli { + #[clap(flatten)] + pub opts: GlobalOpts, + #[command(subcommand)] pub command: Commands, } @@ -138,13 +157,50 @@ async fn build_manager<'a>() -> anyhow::Result> { Ok(ManagerClient::new(conn).await?) } +/// True if use of the remote API is allowed (yes by default when the API is secure, the user is +/// asked if the API is insecure - e.g. when it uses self-signed certificate) +async fn allowed_insecure_api(use_insecure: bool, api_url: String) -> Result { + // fake client used for remote site detection + let mut ping_client = BaseHTTPClient::default(); + ping_client.base_url = api_url; + + // decide whether access to remote site has to be insecure (self-signed certificate or not) + match ping_client.get::>("/ping").await { + // Problem with http remote API reachability + Err(ServiceError::HTTPError(_)) => Ok(use_insecure || Confirm::new("There was a problem with the remote API and it is treated as insecure. Do you want to continue?") + .with_default(false) + .prompt() + .unwrap_or(false)), + // another error + Err(e) => Err(e), + // success doesn't bother us here + Ok(_) => Ok(false) + } +} + pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { + // somehow check whether we need to ask user for self-signed certificate acceptance + let api_url = cli.opts.api.trim_end_matches('/').to_string(); + + let mut client = BaseHTTPClient::default(); + + client.base_url = api_url.clone(); + + if allowed_insecure_api(cli.opts.insecure, api_url.clone()).await? { + client = client.insecure(); + } + + // we need to distinguish commands on those which assume that authentication JWT is already + // available and those which not (or don't need it) + client = if let Commands::Auth(_) = cli.command { + client.unauthenticated()? + } else { + // this deals with authentication need inside + client.authenticated()? + }; + match cli.command { - Commands::Config(subcommand) => { - let manager = build_manager().await?; - wait_for_services(&manager).await?; - run_config_cmd(subcommand).await? - } + Commands::Config(subcommand) => run_config_cmd(client, subcommand).await?, Commands::Probe => { let manager = build_manager().await?; wait_for_services(&manager).await?; @@ -155,10 +211,14 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { let manager = build_manager().await?; install(&manager, 3).await? } - Commands::Questions(subcommand) => run_questions_cmd(subcommand).await?, + Commands::Questions(subcommand) => run_questions_cmd(client, subcommand).await?, + // TODO: logs command was originally designed with idea that agama's cli and agama + // installation runs on the same machine, so it is unable to do remote connection Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, - Commands::Auth(subcommand) => run_auth_cmd(subcommand).await?, Commands::Download { url } => Transfer::get(&url, std::io::stdout())?, + Commands::Auth(subcommand) => { + run_auth_cmd(client, subcommand).await?; + } }; Ok(()) diff --git a/rust/agama-cli/src/profile.rs b/rust/agama-cli/src/profile.rs index 9585cb4c3..b6950f7df 100644 --- a/rust/agama-cli/src/profile.rs +++ b/rust/agama-cli/src/profile.rs @@ -19,7 +19,7 @@ // find current contact information at www.suse.com. use agama_lib::{ - auth::AuthToken, + base_http_client::BaseHTTPClient, install_settings::InstallSettings, profile::{AutoyastProfile, ProfileEvaluator, ProfileValidator, ValidationResult}, transfer::Transfer, @@ -153,9 +153,7 @@ async fn import(url_string: String, dir: Option) -> anyhow::Result<()> } async fn store_settings>(path: P) -> anyhow::Result<()> { - let token = AuthToken::find().context("You are not logged in")?; - let client = agama_lib::http_client(token.as_str())?; - let store = SettingsStore::new(client).await?; + let store = SettingsStore::new(BaseHTTPClient::default().authenticated()?).await?; let settings = InstallSettings::from_file(&path)?; store.store(&settings).await?; Ok(()) diff --git a/rust/agama-cli/src/questions.rs b/rust/agama-cli/src/questions.rs index a763f1999..e3b6299fd 100644 --- a/rust/agama-cli/src/questions.rs +++ b/rust/agama-cli/src/questions.rs @@ -20,7 +20,7 @@ use agama_lib::proxies::Questions1Proxy; use agama_lib::questions::http_client::HTTPClient; -use agama_lib::{connection, error::ServiceError}; +use agama_lib::{base_http_client::BaseHTTPClient, connection, error::ServiceError}; use clap::{Args, Subcommand, ValueEnum}; // TODO: use for answers also JSON to be consistent @@ -74,8 +74,8 @@ async fn set_answers(proxy: Questions1Proxy<'_>, path: String) -> Result<(), Ser .map_err(|e| e.into()) } -async fn list_questions() -> Result<(), ServiceError> { - let client = HTTPClient::new()?; +async fn list_questions(client: BaseHTTPClient) -> Result<(), ServiceError> { + let client = HTTPClient::new(client)?; let questions = client.list_questions().await?; // FIXME: if performance is bad, we can skip converting json from http to struct and then // serialize it, but it won't be pretty string @@ -85,8 +85,8 @@ async fn list_questions() -> Result<(), ServiceError> { Ok(()) } -async fn ask_question() -> Result<(), ServiceError> { - let client = HTTPClient::new()?; +async fn ask_question(client: BaseHTTPClient) -> Result<(), ServiceError> { + let client = HTTPClient::new(client)?; let question = serde_json::from_reader(std::io::stdin())?; let created_question = client.create_question(&question).await?; @@ -104,14 +104,17 @@ async fn ask_question() -> Result<(), ServiceError> { Ok(()) } -pub async fn run(subcommand: QuestionsCommands) -> Result<(), ServiceError> { +pub async fn run( + client: BaseHTTPClient, + subcommand: QuestionsCommands, +) -> Result<(), ServiceError> { let connection = connection().await?; let proxy = Questions1Proxy::new(&connection).await?; match subcommand { QuestionsCommands::Mode(value) => set_mode(proxy, value.value).await, QuestionsCommands::Answers { path } => set_answers(proxy, path).await, - QuestionsCommands::List => list_questions().await, - QuestionsCommands::Ask => ask_question().await, + QuestionsCommands::List => list_questions(client).await, + QuestionsCommands::Ask => ask_question(client).await, } } diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index 7e2cb59b3..299318773 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -40,9 +40,11 @@ use crate::{auth::AuthToken, error::ServiceError}; /// client.get("/questions").await /// } /// ``` + #[derive(Clone)] pub struct BaseHTTPClient { client: reqwest::Client, + insecure: bool, pub base_url: String, } @@ -55,21 +57,41 @@ impl Default for BaseHTTPClient { fn default() -> Self { Self { client: reqwest::Client::new(), + insecure: false, base_url: API_URL.to_owned(), } } } impl BaseHTTPClient { + /// Allows the client to connect to remote API with insecure certificate (e.g. self-signed) + pub fn insecure(self) -> Self { + Self { + insecure: true, + ..self + } + } + /// Uses `localhost`, authenticates with [`AuthToken`]. - pub fn new() -> Result { + pub fn authenticated(self) -> Result { + Ok(Self { + client: Self::authenticated_client(self.insecure)?, + ..self + }) + } + + /// Configures itself for connection(s) without authentication token + pub fn unauthenticated(self) -> Result { Ok(Self { - client: Self::authenticated_client()?, - ..Default::default() + client: reqwest::Client::builder() + .danger_accept_invalid_certs(self.insecure) + .build() + .map_err(anyhow::Error::new)?, + ..self }) } - fn authenticated_client() -> Result { + fn authenticated_client(insecure: bool) -> Result { // TODO: this error is subtly misleading, leading me to believe the SERVER said it, // but in fact it is the CLIENT not finding an auth token let token = AuthToken::find().ok_or(ServiceError::NotAuthenticated)?; @@ -82,6 +104,7 @@ impl BaseHTTPClient { headers.insert(header::AUTHORIZATION, value); let client = reqwest::Client::builder() + .danger_accept_invalid_certs(insecure) .default_headers(headers) .build()?; Ok(client) diff --git a/rust/agama-lib/src/localization/http_client.rs b/rust/agama-lib/src/localization/http_client.rs index 33950ab1c..5e24e46aa 100644 --- a/rust/agama-lib/src/localization/http_client.rs +++ b/rust/agama-lib/src/localization/http_client.rs @@ -26,13 +26,7 @@ pub struct LocalizationHTTPClient { } impl LocalizationHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Result { + pub fn new(base: BaseHTTPClient) -> Result { Ok(Self { client: base }) } diff --git a/rust/agama-lib/src/localization/store.rs b/rust/agama-lib/src/localization/store.rs index c4294df54..7946e8476 100644 --- a/rust/agama-lib/src/localization/store.rs +++ b/rust/agama-lib/src/localization/store.rs @@ -22,6 +22,7 @@ // TODO: for an overview see crate::store (?) use super::{LocalizationHTTPClient, LocalizationSettings}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::localization::model::LocaleConfig; @@ -31,9 +32,9 @@ pub struct LocalizationStore { } impl LocalizationStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - localization_client: LocalizationHTTPClient::new()?, + localization_client: LocalizationHTTPClient::new(client)?, }) } @@ -100,7 +101,7 @@ mod test { ) -> Result { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = LocalizationHTTPClient::new_with_base(bhc)?; + let client = LocalizationHTTPClient::new(bhc)?; LocalizationStore::new_with_client(client) } diff --git a/rust/agama-lib/src/manager/http_client.rs b/rust/agama-lib/src/manager/http_client.rs index e3b1b4eb1..7cfd373fd 100644 --- a/rust/agama-lib/src/manager/http_client.rs +++ b/rust/agama-lib/src/manager/http_client.rs @@ -25,13 +25,7 @@ pub struct ManagerHTTPClient { } impl ManagerHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index 058f2b96f..44dcbfee9 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -19,72 +19,42 @@ // find current contact information at www.suse.com. use super::{settings::NetworkConnection, types::Device}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; -use reqwest::{Client, Response}; -use serde_json; - -const API_URL: &str = "http://localhost/api/network"; /// HTTP/JSON client for the network service pub struct NetworkClient { - pub client: Client, + pub client: BaseHTTPClient, } impl NetworkClient { - pub async fn new(client: Client) -> Result { + pub async fn new(client: BaseHTTPClient) -> Result { Ok(Self { client }) } - async fn text_for(&self, response: Response) -> Result { - let status = response.status(); - let text = response - .text() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; - - if status != 200 { - return Err(ServiceError::NetworkClientError(text)); - } - - Ok(text) - } - - async fn get(&self, path: &str) -> Result { - let response = self - .client - .get(format!("{API_URL}{path}")) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; - - self.text_for(response).await - } - /// Returns an array of network devices pub async fn devices(&self) -> Result, ServiceError> { - let text = self.get("/devices").await?; - - let json: Vec = serde_json::from_str(&text) - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let json = self.client.get::>("/network/devices").await?; Ok(json) } /// Returns an array of network connections pub async fn connections(&self) -> Result, ServiceError> { - let text = self.get("/connections").await?; - - let json: Vec = serde_json::from_str(&text) - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let json = self + .client + .get::>("/network/connections") + .await?; Ok(json) } /// Returns an array of network connections pub async fn connection(&self, id: &str) -> Result { - let text = self.get(format!("/connections/{id}").as_str()).await?; - let json: NetworkConnection = serde_json::from_str(&text) - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let json = self + .client + .get::(format!("/network/connections/{id}").as_str()) + .await?; Ok(json) } @@ -98,20 +68,12 @@ impl NetworkClient { let response = self.connection(id.as_str()).await; if response.is_ok() { - let path = format!("{API_URL}/connections/{id}"); - self.client - .put(path) - .json(&connection) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let path = format!("/network/connections/{id}"); + self.client.put_void(&path.as_str(), &connection).await? } else { self.client - .post(format!("{API_URL}/connections").as_str()) - .json(&connection) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + .post_void(format!("/network/connections").as_str(), &connection) + .await? } Ok(()) @@ -119,11 +81,11 @@ impl NetworkClient { /// Returns an array of network connections pub async fn apply(&self) -> Result<(), ServiceError> { + // trying to be tricky here. If something breaks then we need a put method on + // BaseHTTPClient which doesn't require a serialiable object for the body self.client - .put(format!("{API_URL}/system/apply")) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + .put_void(&format!("/network/system/apply").as_str(), &()) + .await?; Ok(()) } diff --git a/rust/agama-lib/src/network/store.rs b/rust/agama-lib/src/network/store.rs index 59b513734..3b7378ad5 100644 --- a/rust/agama-lib/src/network/store.rs +++ b/rust/agama-lib/src/network/store.rs @@ -19,6 +19,7 @@ // find current contact information at www.suse.com. use super::settings::NetworkConnection; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::network::{NetworkClient, NetworkSettings}; @@ -28,7 +29,7 @@ pub struct NetworkStore { } impl NetworkStore { - pub async fn new(client: reqwest::Client) -> Result { + pub async fn new(client: BaseHTTPClient) -> Result { Ok(Self { network_client: NetworkClient::new(client).await?, }) diff --git a/rust/agama-lib/src/product/http_client.rs b/rust/agama-lib/src/product/http_client.rs index eaf0f8fba..424a8b49f 100644 --- a/rust/agama-lib/src/product/http_client.rs +++ b/rust/agama-lib/src/product/http_client.rs @@ -28,13 +28,7 @@ pub struct ProductHTTPClient { } impl ProductHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/product/store.rs b/rust/agama-lib/src/product/store.rs index a6dc90209..d43a6166c 100644 --- a/rust/agama-lib/src/product/store.rs +++ b/rust/agama-lib/src/product/store.rs @@ -20,6 +20,7 @@ //! Implements the store for the product settings. use super::{ProductHTTPClient, ProductSettings}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::manager::http_client::ManagerHTTPClient; @@ -30,10 +31,10 @@ pub struct ProductStore { } impl ProductStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - product_client: ProductHTTPClient::new()?, - manager_client: ManagerHTTPClient::new()?, + product_client: ProductHTTPClient::new(client.clone()), + manager_client: ManagerHTTPClient::new(client.clone()), }) } @@ -100,8 +101,8 @@ mod test { fn product_store(mock_server_url: String) -> ProductStore { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let p_client = ProductHTTPClient::new_with_base(bhc.clone()); - let m_client = ManagerHTTPClient::new_with_base(bhc); + let p_client = ProductHTTPClient::new(bhc.clone()); + let m_client = ManagerHTTPClient::new(bhc); ProductStore { product_client: p_client, manager_client: m_client, diff --git a/rust/agama-lib/src/questions/http_client.rs b/rust/agama-lib/src/questions/http_client.rs index d2ad37b1d..631e78a06 100644 --- a/rust/agama-lib/src/questions/http_client.rs +++ b/rust/agama-lib/src/questions/http_client.rs @@ -32,10 +32,8 @@ pub struct HTTPClient { } impl HTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) + pub fn new(client: BaseHTTPClient) -> Result { + Ok(Self { client: client }) } pub async fn list_questions(&self) -> Result, ServiceError> { diff --git a/rust/agama-lib/src/software/http_client.rs b/rust/agama-lib/src/software/http_client.rs index 6bb377787..4a770e6af 100644 --- a/rust/agama-lib/src/software/http_client.rs +++ b/rust/agama-lib/src/software/http_client.rs @@ -27,13 +27,7 @@ pub struct SoftwareHTTPClient { } impl SoftwareHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/software/store.rs b/rust/agama-lib/src/software/store.rs index a152a4917..dbe4c6f3f 100644 --- a/rust/agama-lib/src/software/store.rs +++ b/rust/agama-lib/src/software/store.rs @@ -23,6 +23,7 @@ use std::collections::HashMap; use super::{SoftwareHTTPClient, SoftwareSettings}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; /// Loads and stores the software settings from/to the D-Bus service. @@ -31,9 +32,9 @@ pub struct SoftwareStore { } impl SoftwareStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - software_client: SoftwareHTTPClient::new()?, + software_client: SoftwareHTTPClient::new(client), }) } @@ -65,7 +66,7 @@ mod test { fn software_store(mock_server_url: String) -> SoftwareStore { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = SoftwareHTTPClient::new_with_base(bhc); + let client = SoftwareHTTPClient::new(bhc); SoftwareStore { software_client: client, } diff --git a/rust/agama-lib/src/storage/http_client.rs b/rust/agama-lib/src/storage/http_client.rs index 6be50dbf9..402dc261a 100644 --- a/rust/agama-lib/src/storage/http_client.rs +++ b/rust/agama-lib/src/storage/http_client.rs @@ -28,13 +28,7 @@ pub struct StorageHTTPClient { } impl StorageHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/storage/store.rs b/rust/agama-lib/src/storage/store.rs index 5b8935e28..6f835bc4c 100644 --- a/rust/agama-lib/src/storage/store.rs +++ b/rust/agama-lib/src/storage/store.rs @@ -21,6 +21,7 @@ //! Implements the store for the storage settings. use super::StorageSettings; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::storage::http_client::StorageHTTPClient; @@ -30,9 +31,9 @@ pub struct StorageStore { } impl StorageStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - storage_client: StorageHTTPClient::new()?, + storage_client: StorageHTTPClient::new(client), }) } @@ -57,7 +58,7 @@ mod test { fn storage_store(mock_server_url: String) -> StorageStore { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = StorageHTTPClient::new_with_base(bhc); + let client = StorageHTTPClient::new(bhc); StorageStore { storage_client: client, } diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 83ac06275..c29145615 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -21,6 +21,7 @@ //! Load/store the settings from/to the D-Bus services. // TODO: quickly explain difference between FooSettings and FooStore, with an example +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::install_settings::InstallSettings; use crate::{ @@ -44,14 +45,14 @@ pub struct Store { } impl Store { - pub async fn new(http_client: reqwest::Client) -> Result { + pub async fn new(http_client: BaseHTTPClient) -> Result { Ok(Self { - localization: LocalizationStore::new()?, - users: UsersStore::new()?, - network: NetworkStore::new(http_client).await?, - product: ProductStore::new()?, - software: SoftwareStore::new()?, - storage: StorageStore::new()?, + localization: LocalizationStore::new(http_client.clone())?, + users: UsersStore::new(http_client.clone())?, + network: NetworkStore::new(http_client.clone()).await?, + product: ProductStore::new(http_client.clone())?, + software: SoftwareStore::new(http_client.clone())?, + storage: StorageStore::new(http_client.clone())?, }) } diff --git a/rust/agama-lib/src/users/http_client.rs b/rust/agama-lib/src/users/http_client.rs index 9db72ed93..fa867fb3d 100644 --- a/rust/agama-lib/src/users/http_client.rs +++ b/rust/agama-lib/src/users/http_client.rs @@ -27,13 +27,7 @@ pub struct UsersHTTPClient { } impl UsersHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(client: BaseHTTPClient) -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { client }) } diff --git a/rust/agama-lib/src/users/store.rs b/rust/agama-lib/src/users/store.rs index 54087f493..1511a00a4 100644 --- a/rust/agama-lib/src/users/store.rs +++ b/rust/agama-lib/src/users/store.rs @@ -19,6 +19,7 @@ // find current contact information at www.suse.com. use super::{FirstUser, FirstUserSettings, RootUserSettings, UserSettings, UsersHTTPClient}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; /// Loads and stores the users settings from/to the D-Bus service. @@ -27,9 +28,9 @@ pub struct UsersStore { } impl UsersStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - users_client: UsersHTTPClient::new()?, + users_client: UsersHTTPClient::new(client)?, }) } @@ -109,7 +110,7 @@ mod test { fn users_store(mock_server_url: String) -> Result { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = UsersHTTPClient::new_with_base(bhc)?; + let client = UsersHTTPClient::new(bhc)?; UsersStore::new_with_client(client) } diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 2fedadd04..41813d410 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Oct 16 07:55:27 UTC 2024 - Michal Filka + +- Implemented option for providing remote API address for the CLI + gh#agama-project/agama#1495 + ------------------------------------------------------------------- Mon Oct 14 13:53:10 UTC 2024 - Josef Reidinger