From a92b896d17d53c19bf2a826d07db6f8571330485 Mon Sep 17 00:00:00 2001 From: Maxime Van Hees Date: Tue, 22 Jul 2025 17:34:17 +0200 Subject: [PATCH] added functions check auctioned transactions and order auctioned server --- examples/server_ordering.rhai | 43 ++- examples/ssh_key_management.rhai | 5 +- src/api/mod.rs | 112 ++++++- src/api/models.rs | 278 ++++++++++++++++++ src/scripting/mod.rs | 15 +- src/scripting/printing/mod.rs | 6 +- .../printing/server_ordering_table.rs | 178 +++++++++++ src/scripting/server_ordering.rs | 112 ++++++- 8 files changed, 724 insertions(+), 25 deletions(-) diff --git a/examples/server_ordering.rhai b/examples/server_ordering.rhai index b328f49..997267d 100644 --- a/examples/server_ordering.rhai +++ b/examples/server_ordering.rhai @@ -1,15 +1,48 @@ -// // Get all available products (servers) that we can order and print them in a table +/// --- Get all available products (servers) that we can order and print them in a table // let available_server_products = hetzner.get_server_ordering_product_overview(); // available_server_products.pretty_print(); -// // List the details from a specific sever product based on the ID +/// --- List the details from a specific sever product based on the ID // let example_server_product = hetzner.get_server_ordering_product_by_id("AX41-NVMe"); // print(example_server_product); -// List all the transactions from the past 30 days +/// --- List all the transactions from the past 30 days // let transactions_last_30 = hetzner.get_transactions(); // print(transactions_last_30); -let example_transaction = hetzner.get_transaction_by_id("2111181"); -print(example_transaction); +/// --- Fetch a transcation by ID +// let example_transaction = hetzner.get_transaction_by_id("120000706572"); +// print(example_transaction); + +/// --- List all the auction transaction from the past 30 days +// let auction_transactions_last_30 = hetzner.get_auction_transactions(); +// auction_transactions_last_30.pretty_print(); + +/// --- Fetch a auction transaction by ID +// let example_auction_transaction = hetzner.get_auction_transaction_by_id(""); +// print(example_auction_transaction); + +/// --- List all the auctioned server products +// let auctioned_servers = hetzner.get_auction_server_products(); +// auctioned_servers.pretty_print(); + +/// --- Get information about one specific auctioned server by ID +let auctioned_server = hetzner.get_auction_server_product_by_id("2739567"); +print(auctioned_server); + +/// --- Order an auction server +// 1. Grab the SSH key to pass to the deployment +let ssh_key = hetzner.get_ssh_key("e0:73:80:26:80:46:f0:c8:bb:74:f4:d0:2d:10:2d:6f"); +// 2. Order the auctioned server +let transaction = hetzner.order_auction_server( + auctioned_server.id, + [ssh_key], // Pass ssh_key as an array + (), // dist (Option) + (), // arch (Option) + (), // lang (Option) + (), // comment (Option) + [], // addons (Array) + (), // test (Option) +); +print(transaction); diff --git a/examples/ssh_key_management.rhai b/examples/ssh_key_management.rhai index 68bab33..a1bad1c 100644 --- a/examples/ssh_key_management.rhai +++ b/examples/ssh_key_management.rhai @@ -8,9 +8,8 @@ keys.pretty_print(); // print(key); // Add a new SSH key -// Replace "my-new-key" with the desired name and "ssh-rsa ..." with your public key data -// let new_key = hetzner.add_ssh_key("vanheesm@incubaid.com", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFyZJCEsvRc0eitsOoq+ywC5Lmqejvk3hXMVbO0AxPrd"); -// print(new_key); +let new_key = hetzner.add_ssh_key("vanheesm@incubaid.com", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFyZJCEsvRc0eitsOoq+ywC5Lmqejvk3hXMVbO0AxPrd"); +print(new_key); // Update an SSH key's name // Replace "cb:8b:ef:a7:fe:04:87:3f:e5:55:cd:12:e3:e8:9f:99" with the fingerprint of the key you want to update diff --git a/src/api/mod.rs b/src/api/mod.rs index 4ede250..6391506 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -4,7 +4,7 @@ pub mod models; use self::models::{Boot, Rescue, Server, SshKey}; use crate::api::error::ApiError; use crate::api::models::{ - BootWrapper, Cancellation, CancellationWrapper, OrderServerProduct, OrderServerProductWrapper, RescueWrapped, ServerWrapper, SshKeyWrapper, Transaction, TransactionWrapper + AuctionServerProduct, AuctionServerProductWrapper, AuctionTransaction, AuctionTransactionWrapper, BootWrapper, Cancellation, CancellationWrapper, OrderServerProduct, OrderServerProductWrapper, RescueWrapped, ServerWrapper, SshKeyWrapper, Transaction, TransactionWrapper }; use crate::config::Config; use error::AppError; @@ -246,7 +246,9 @@ impl Client { Ok(wrapped.rescue) } - pub fn get_server_ordering_product_overview(&self) -> Result, AppError> { + pub fn get_server_products( + &self, + ) -> Result, AppError> { let response = self .http_client .get(format!("{}/order/server/product", &self.config.api_url)) @@ -258,10 +260,16 @@ impl Client { Ok(products) } - pub fn get_server_ordering_product_by_id(&self, product_id: &str) -> Result { + pub fn get_server_product_by_id( + &self, + product_id: &str, + ) -> Result { let response = self .http_client - .get(format!("{}/order/server/product/{}", &self.config.api_url, product_id)) + .get(format!( + "{}/order/server/product/{}", + &self.config.api_url, product_id + )) .basic_auth(&self.config.username, Some(&self.config.password)) .send()?; @@ -301,7 +309,10 @@ impl Client { pub fn get_transaction_by_id(&self, transaction_id: &str) -> Result { let response = self .http_client - .get(format!("{}/order/server/transaction/{}", &self.config.api_url, transaction_id)) + .get(format!( + "{}/order/server/transaction/{}", + &self.config.api_url, transaction_id + )) .basic_auth(&self.config.username, Some(&self.config.password)) .send()?; @@ -319,4 +330,95 @@ impl Client { let transactions = wrapped.into_iter().map(|t| t.transaction).collect(); Ok(transactions) } + pub fn get_auction_server_products(&self) -> Result, AppError> { + let response = self + .http_client + .get(format!( + "{}/order/server_market/product", + &self.config.api_url + )) + .basic_auth(&self.config.username, Some(&self.config.password)) + .send()?; + + let wrapped: Vec = self.handle_response(response)?; + let products = wrapped.into_iter().map(|asp| asp.product).collect(); + Ok(products) + } + pub fn get_auction_server_product_by_id(&self, product_id: &str) -> Result { + let response = self + .http_client + .get(format!("{}/order/server_market/product/{}", &self.config.api_url, product_id)) + .basic_auth(&self.config.username, Some(&self.config.password)) + .send()?; + + let wrapped: AuctionServerProductWrapper = self.handle_response(response)?; + Ok(wrapped.product) + } + pub fn get_auction_transactions(&self) -> Result, AppError> { + let response = self + .http_client + .get(format!("{}/order/server_market/transaction", &self.config.api_url)) + .basic_auth(&self.config.username, Some(&self.config.password)) + .send()?; + + let wrapped: Vec = self.handle_response(response)?; + let transactions = wrapped.into_iter().map(|t| t.transaction).collect(); + Ok(transactions) + } + + pub fn get_auction_transaction_by_id(&self, transaction_id: &str) -> Result { + let response = self + .http_client + .get(format!("{}/order/server_market/transaction/{}", &self.config.api_url, transaction_id)) + .basic_auth(&self.config.username, Some(&self.config.password)) + .send()?; + + let wrapped: AuctionTransactionWrapper = self.handle_response(response)?; + Ok(wrapped.transaction) + } + pub fn order_auction_server( + &self, + product_id: i32, + authorized_keys: Vec, + dist: Option, + arch: Option, + lang: Option, + comment: Option, + addons: Option>, + test: Option, + ) -> Result { + let mut params = json!({ + "product_id": product_id, + "authorized_key": authorized_keys, + }); + + if let Some(dist) = dist { + params["dist"] = json!(dist); + } + if let Some(arch) = arch { + params["@deprecated arch"] = json!(arch); + } + if let Some(lang) = lang { + params["lang"] = json!(lang); + } + if let Some(comment) = comment { + params["comment"] = json!(comment); + } + if let Some(addons) = addons { + params["addon"] = json!(addons); + } + if let Some(test) = test { + params["test"] = json!(test); + } + + let response = self + .http_client + .post(format!("{}/order/server_market/transaction", &self.config.api_url)) + .basic_auth(&self.config.username, Some(&self.config.password)) + .json(¶ms) + .send()?; + + let wrapped: AuctionTransactionWrapper = self.handle_response(response)?; + Ok(wrapped.transaction) + } } diff --git a/src/api/models.rs b/src/api/models.rs index 9f1af5a..f6448f0 100644 --- a/src/api/models.rs +++ b/src/api/models.rs @@ -999,4 +999,282 @@ impl HostKey { .with_get("key_type", |k: &mut HostKey| k.key_type.clone()) .with_get("size", |k: &mut HostKey| k.size); } +} + +#[derive(Debug, Deserialize, Clone)] +pub struct AuctionServerProductWrapper { + pub product: AuctionServerProduct, +} + +#[derive(Debug, Deserialize, Clone, CustomType)] +#[rhai_type(extra = Self::build_rhai_type)] +pub struct AuctionServerProduct { + pub id: i32, + pub name: String, + #[serde(deserialize_with = "string_or_seq_string")] + pub description: Vec, + pub traffic: String, + #[serde(deserialize_with = "string_or_seq_string")] + pub dist: Vec, + #[serde(rename = "@deprecated arch", default, deserialize_with = "option_string_or_seq_string")] + #[deprecated(note = "use `dist` instead")] + pub arch: Option>, + #[serde(deserialize_with = "string_or_seq_string")] + pub lang: Vec, + pub cpu: String, + pub cpu_benchmark: i32, + pub memory_size: i32, + pub hdd_size: i32, + pub hdd_text: String, + pub hdd_count: i32, + pub datacenter: String, + pub network_speed: String, + pub price: String, + pub price_hourly: Option, + pub price_setup: String, + #[serde(rename = "price_vat")] + pub price_with_vat: String, + #[serde(rename = "price_hourly_vat")] + pub price_hourly_with_vat: Option, + #[serde(rename = "price_setup_vat")] + pub price_setup_with_vat: String, + pub fixed_price: bool, + pub next_reduce: i32, + pub next_reduce_date: String, + pub orderable_addons: Vec, +} + +impl AuctionServerProduct { + fn build_rhai_type(builder: &mut TypeBuilder) { + builder + .with_name("AuctionServerProduct") + .with_get("id", |p: &mut AuctionServerProduct| p.id) + .with_get("name", |p: &mut AuctionServerProduct| p.name.clone()) + .with_get("description", |p: &mut AuctionServerProduct| p.description.clone()) + .with_get("traffic", |p: &mut AuctionServerProduct| p.traffic.clone()) + .with_get("dist", |p: &mut AuctionServerProduct| p.dist.clone()) + .with_get("arch", |p: &mut AuctionServerProduct| p.arch.clone()) + .with_get("lang", |p: &mut AuctionServerProduct| p.lang.clone()) + .with_get("cpu", |p: &mut AuctionServerProduct| p.cpu.clone()) + .with_get("cpu_benchmark", |p: &mut AuctionServerProduct| p.cpu_benchmark) + .with_get("memory_size", |p: &mut AuctionServerProduct| p.memory_size) + .with_get("hdd_size", |p: &mut AuctionServerProduct| p.hdd_size) + .with_get("hdd_text", |p: &mut AuctionServerProduct| p.hdd_text.clone()) + .with_get("hdd_count", |p: &mut AuctionServerProduct| p.hdd_count) + .with_get("datacenter", |p: &mut AuctionServerProduct| p.datacenter.clone()) + .with_get("network_speed", |p: &mut AuctionServerProduct| p.network_speed.clone()) + .with_get("price", |p: &mut AuctionServerProduct| p.price.clone()) + .with_get("price_hourly", |p: &mut AuctionServerProduct| p.price_hourly.clone()) + .with_get("price_setup", |p: &mut AuctionServerProduct| p.price_setup.clone()) + .with_get("price_with_vat", |p: &mut AuctionServerProduct| p.price_with_vat.clone()) + .with_get("price_hourly_with_vat", |p: &mut AuctionServerProduct| p.price_hourly_with_vat.clone()) + .with_get("price_setup_with_vat", |p: &mut AuctionServerProduct| p.price_setup_with_vat.clone()) + .with_get("fixed_price", |p: &mut AuctionServerProduct| p.fixed_price) + .with_get("next_reduce", |p: &mut AuctionServerProduct| p.next_reduce) + .with_get("next_reduce_date", |p: &mut AuctionServerProduct| p.next_reduce_date.clone()) + .with_get("orderable_addons", |p: &mut AuctionServerProduct| p.orderable_addons.clone()) + .on_print(|p: &mut AuctionServerProduct| p.to_string()) + .with_fn("pretty_print", |p: &mut AuctionServerProduct| p.to_string()); + } +} + +impl fmt::Display for AuctionServerProduct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut table = Table::new(); + table.add_row(row!["Property", "Value"]); + table.add_row(row!["ID", self.id.to_string()]); + table.add_row(row!["Name", self.name.clone()]); + table.add_row(row!["Description", self.description.join(", ")]); + table.add_row(row!["Traffic", self.traffic.clone()]); + table.add_row(row!["Distributions", self.dist.join(", ")]); + table.add_row(row!["Architectures", self.arch.as_deref().unwrap_or_default().join(", ")]); + table.add_row(row!["Languages", self.lang.join(", ")]); + table.add_row(row!["CPU", self.cpu.clone()]); + table.add_row(row!["CPU Benchmark", self.cpu_benchmark.to_string()]); + table.add_row(row!["Memory Size (GB)", self.memory_size.to_string()]); + table.add_row(row!["HDD Size (GB)", self.hdd_size.to_string()]); + table.add_row(row!["HDD Text", self.hdd_text.clone()]); + table.add_row(row!["HDD Count", self.hdd_count.to_string()]); + table.add_row(row!["Datacenter", self.datacenter.clone()]); + table.add_row(row!["Network Speed", self.network_speed.clone()]); + table.add_row(row!["Price (Net)", self.price.clone()]); + table.add_row(row!["Price (Hourly Net)", self.price_hourly.as_deref().unwrap_or("N/A").to_string()]); + table.add_row(row!["Price (Setup Net)", self.price_setup.clone()]); + table.add_row(row!["Price (VAT)", self.price_with_vat.clone()]); + table.add_row(row!["Price (Hourly VAT)", self.price_hourly_with_vat.as_deref().unwrap_or("N/A").to_string()]); + table.add_row(row!["Price (Setup VAT)", self.price_setup_with_vat.clone()]); + table.add_row(row!["Fixed Price", self.fixed_price.to_string()]); + table.add_row(row!["Next Reduce (seconds)", self.next_reduce.to_string()]); + table.add_row(row!["Next Reduce Date", self.next_reduce_date.clone()]); + + let mut addons_table = Table::new(); + addons_table.add_row(row![b => "ID", "Name", "Min", "Max", "Prices"]); + for addon in &self.orderable_addons { + let mut addon_prices_table = Table::new(); + addon_prices_table.add_row(row![b => "Location", "Net", "Gross", "Hourly Net", "Hourly Gross", "Setup Net", "Setup Gross"]); + for price in &addon.prices { + addon_prices_table.add_row(row![ + price.location, + price.price.net, + price.price.gross, + price.price.hourly_net, + price.price.hourly_gross, + price.price_setup.net, + price.price_setup.gross + ]); + } + addons_table.add_row(row![ + addon.id, + addon.name, + addon.min, + addon.max, + addon_prices_table + ]); + } + table.add_row(row!["Orderable Addons", addons_table]); + write!(f, "{}", table) + } +} + +#[derive(Debug, Deserialize, Clone)] +pub struct AuctionTransactionWrapper { + pub transaction: AuctionTransaction, +} + +#[derive(Debug, Deserialize, Clone, CustomType)] +#[rhai_type(extra = Self::build_rhai_type)] +pub struct AuctionTransaction { + pub id: String, + pub date: String, + pub status: String, + pub server_number: Option, + pub server_ip: Option, + pub authorized_key: Vec, + pub host_key: Vec, + pub comment: Option, + pub product: AuctionTransactionProduct, + pub addons: Vec, +} + +#[derive(Debug, Deserialize, Clone, CustomType)] +#[rhai_type(extra = Self::build_rhai_type)] +pub struct AuctionTransactionProduct { + pub id: i32, + pub name: String, + pub description: Vec, + pub traffic: String, + pub dist: String, + #[serde(rename = "@deprecated arch")] + pub arch: String, + pub lang: String, + pub cpu: String, + pub cpu_benchmark: i32, + pub memory_size: i32, + pub hdd_size: i32, + pub hdd_text: String, + pub hdd_count: i32, + pub datacenter: String, + pub network_speed: String, + pub fixed_price: bool, + pub next_reduce: i32, + pub next_reduce_date: String, +} + +impl AuctionTransaction { + fn build_rhai_type(builder: &mut TypeBuilder) { + builder + .with_name("AuctionTransaction") + .with_get("id", |t: &mut AuctionTransaction| t.id.clone()) + .with_get("date", |t: &mut AuctionTransaction| t.date.clone()) + .with_get("status", |t: &mut AuctionTransaction| t.status.clone()) + .with_get("server_number", |t: &mut AuctionTransaction| t.server_number) + .with_get("server_ip", |t: &mut AuctionTransaction| t.server_ip.clone()) + .with_get("authorized_key", |t: &mut AuctionTransaction| t.authorized_key.clone()) + .with_get("host_key", |t: &mut AuctionTransaction| t.host_key.clone()) + .with_get("comment", |t: &mut AuctionTransaction| t.comment.clone()) + .with_get("product", |t: &mut AuctionTransaction| t.product.clone()) + .with_get("addons", |t: &mut AuctionTransaction| t.addons.clone()); + } +} + +impl AuctionTransactionProduct { + fn build_rhai_type(builder: &mut TypeBuilder) { + builder + .with_name("AuctionTransactionProduct") + .with_get("id", |p: &mut AuctionTransactionProduct| p.id) + .with_get("name", |p: &mut AuctionTransactionProduct| p.name.clone()) + .with_get("description", |p: &mut AuctionTransactionProduct| p.description.clone()) + .with_get("traffic", |p: &mut AuctionTransactionProduct| p.traffic.clone()) + .with_get("dist", |p: &mut AuctionTransactionProduct| p.dist.clone()) + .with_get("arch", |p: &mut AuctionTransactionProduct| p.arch.clone()) + .with_get("lang", |p: &mut AuctionTransactionProduct| p.lang.clone()) + .with_get("cpu", |p: &mut AuctionTransactionProduct| p.cpu.clone()) + .with_get("cpu_benchmark", |p: &mut AuctionTransactionProduct| p.cpu_benchmark) + .with_get("memory_size", |p: &mut AuctionTransactionProduct| p.memory_size) + .with_get("hdd_size", |p: &mut AuctionTransactionProduct| p.hdd_size) + .with_get("hdd_text", |p: &mut AuctionTransactionProduct| p.hdd_text.clone()) + .with_get("hdd_count", |p: &mut AuctionTransactionProduct| p.hdd_count) + .with_get("datacenter", |p: &mut AuctionTransactionProduct| p.datacenter.clone()) + .with_get("network_speed", |p: &mut AuctionTransactionProduct| p.network_speed.clone()) + .with_get("fixed_price", |p: &mut AuctionTransactionProduct| p.fixed_price) + .with_get("next_reduce", |p: &mut AuctionTransactionProduct| p.next_reduce) + .with_get("next_reduce_date", |p: &mut AuctionTransactionProduct| p.next_reduce_date.clone()); + } +} + +impl fmt::Display for AuctionTransaction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut table = Table::new(); + table.add_row(row!["Property", "Value"]); + table.add_row(row!["ID", self.id.clone()]); + table.add_row(row!["Date", self.date.clone()]); + table.add_row(row!["Status", self.status.clone()]); + table.add_row(row!["Server Number", self.server_number.map_or("N/A".to_string(), |id| id.to_string())]); + table.add_row(row!["Server IP", self.server_ip.as_deref().unwrap_or("N/A").to_string()]); + table.add_row(row!["Comment", self.comment.as_deref().unwrap_or("N/A").to_string()]); + table.add_row(row!["Product ID", self.product.id.to_string()]); + table.add_row(row!["Product Name", self.product.name.clone()]); + table.add_row(row!["Product Description", self.product.description.join(", ")]); + table.add_row(row!["Product Traffic", self.product.traffic.clone()]); + table.add_row(row!["Product Distributions", self.product.dist.clone()]); + table.add_row(row!["Product Architectures", self.product.arch.clone()]); + table.add_row(row!["Product Languages", self.product.lang.clone()]); + table.add_row(row!["Product CPU", self.product.cpu.clone()]); + table.add_row(row!["Product CPU Benchmark", self.product.cpu_benchmark.to_string()]); + table.add_row(row!["Product Memory Size (GB)", self.product.memory_size.to_string()]); + table.add_row(row!["Product HDD Size (GB)", self.product.hdd_size.to_string()]); + table.add_row(row!["Product HDD Text", self.product.hdd_text.clone()]); + table.add_row(row!["Product HDD Count", self.product.hdd_count.to_string()]); + table.add_row(row!["Product Datacenter", self.product.datacenter.clone()]); + table.add_row(row!["Product Network Speed", self.product.network_speed.clone()]); + table.add_row(row!["Product Fixed Price", self.product.fixed_price.to_string()]); + table.add_row(row!["Product Next Reduce (seconds)", self.product.next_reduce.to_string()]); + table.add_row(row!["Product Next Reduce Date", self.product.next_reduce_date.clone()]); + table.add_row(row!["Addons", self.addons.join(", ")]); + + let mut authorized_keys_table = Table::new(); + authorized_keys_table.add_row(row![b => "Name", "Fingerprint", "Type", "Size"]); + for key in &self.authorized_key { + authorized_keys_table.add_row(row![ + key.key.name, + key.key.fingerprint, + key.key.key_type, + key.key.size + ]); + } + table.add_row(row!["Authorized Keys", authorized_keys_table]); + + let mut host_keys_table = Table::new(); + host_keys_table.add_row(row![b => "Fingerprint", "Type", "Size"]); + for key in &self.host_key { + host_keys_table.add_row(row![ + key.key.fingerprint, + key.key.key_type, + key.key.size + ]); + } + table.add_row(row!["Host Keys", host_keys_table]); + + write!(f, "{}", table) + } } \ No newline at end of file diff --git a/src/scripting/mod.rs b/src/scripting/mod.rs index 13646d3..43d3fc2 100644 --- a/src/scripting/mod.rs +++ b/src/scripting/mod.rs @@ -1,12 +1,16 @@ use crate::api::Client; -use crate::api::models::{Rescue, Linux, Vnc, Windows, Plesk, Cpanel, Boot, Server, SshKey, Cancellation, OrderServerProduct, Transaction, AuthorizedKey, TransactionProduct, HostKey}; +use crate::api::models::{ + AuctionServerProduct, AuctionTransaction, AuctionTransactionProduct, AuthorizedKey, Boot, + Cancellation, Cpanel, HostKey, Linux, OrderServerProduct, Plesk, Rescue, Server, SshKey, + Transaction, TransactionProduct, Vnc, Windows, +}; use rhai::{Engine, Scope}; -pub mod server; -pub mod ssh_keys; pub mod boot; pub mod printing; +pub mod server; pub mod server_ordering; +pub mod ssh_keys; pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) { let mut engine = Engine::new(); @@ -27,6 +31,9 @@ pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) { engine.build_type::(); engine.build_type::(); engine.build_type::(); + engine.build_type::(); + engine.build_type::(); + engine.build_type::(); server::register(&mut engine); ssh_keys::register(&mut engine); @@ -37,4 +44,4 @@ pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) { scope.push("hetzner", client); (engine, scope) -} \ No newline at end of file +} diff --git a/src/scripting/printing/mod.rs b/src/scripting/printing/mod.rs index a68d4f7..e4ccf5b 100644 --- a/src/scripting/printing/mod.rs +++ b/src/scripting/printing/mod.rs @@ -1,5 +1,5 @@ use rhai::{Array, Engine}; -use crate::{api::models::OrderServerProduct, scripting::{Server, SshKey}}; +use crate::{api::models::{OrderServerProduct, AuctionServerProduct, AuctionTransaction}, scripting::{Server, SshKey}}; mod servers_table; mod ssh_keys_table; @@ -22,6 +22,10 @@ pub fn pretty_print_dispatch(array: Array) { } else if first.is::() { server_ordering_table::pretty_print_server_products(array); + } else if first.is::() { + server_ordering_table::pretty_print_auction_server_products(array); + } else if first.is::() { + server_ordering_table::pretty_print_auction_transactions(array); } else { // Generic fallback for other types for item in array { diff --git a/src/scripting/printing/server_ordering_table.rs b/src/scripting/printing/server_ordering_table.rs index c3cfda0..1c4863d 100644 --- a/src/scripting/printing/server_ordering_table.rs +++ b/src/scripting/printing/server_ordering_table.rs @@ -35,4 +35,182 @@ pub fn pretty_print_server_products(products: rhai::Array) { } } table.printstd(); +} + +pub fn pretty_print_auction_server_products(products: rhai::Array) { + let mut table = Table::new(); + table.add_row(row![b => + "ID", + "Name", + "Description", + "Traffic", + "Distributions", + "Architectures", + "Languages", + "CPU", + "CPU Benchmark", + "Memory Size (GB)", + "HDD Size (GB)", + "HDD Text", + "HDD Count", + "Datacenter", + "Network Speed", + "Price (Net)", + "Price (Hourly Net)", + "Price (Setup Net)", + "Price (VAT)", + "Price (Hourly VAT)", + "Price (Setup VAT)", + "Fixed Price", + "Next Reduce (seconds)", + "Next Reduce Date", + "Orderable Addons", + ]); + + for product_dyn in products { + if let Some(product) = product_dyn.try_cast::() { + let mut addons_table = Table::new(); + addons_table.add_row(row![b => "ID", "Name", "Min", "Max", "Prices"]); + for addon in &product.orderable_addons { + let mut addon_prices_table = Table::new(); + addon_prices_table.add_row(row![b => "Location", "Net", "Gross", "Hourly Net", "Hourly Gross", "Setup Net", "Setup Gross"]); + for price in &addon.prices { + addon_prices_table.add_row(row![ + price.location, + price.price.net, + price.price.gross, + price.price.hourly_net, + price.price.hourly_gross, + price.price_setup.net, + price.price_setup.gross + ]); + } + addons_table.add_row(row![ + addon.id, + addon.name, + addon.min, + addon.max, + addon_prices_table + ]); + } + + table.add_row(row![ + product.id, + product.name, + product.description.join(", "), + product.traffic, + product.dist.join(", "), + product.arch.as_deref().unwrap_or_default().join(", "), + product.lang.join(", "), + product.cpu, + product.cpu_benchmark, + product.memory_size, + product.hdd_size, + product.hdd_text, + product.hdd_count, + product.datacenter, + product.network_speed, + product.price, + product.price_hourly.as_deref().unwrap_or("N/A"), + product.price_setup, + product.price_with_vat, + product.price_hourly_with_vat.as_deref().unwrap_or("N/A"), + product.price_setup_with_vat, + product.fixed_price, + product.next_reduce, + product.next_reduce_date, + addons_table, + ]); + } + } + table.printstd(); +} + +pub fn pretty_print_auction_transactions(transactions: rhai::Array) { + let mut table = Table::new(); + table.add_row(row![b => + "ID", + "Date", + "Status", + "Server Number", + "Server IP", + "Comment", + "Product ID", + "Product Name", + "Product Traffic", + "Product Distributions", + "Product Architectures", + "Product Languages", + "Product CPU", + "Product CPU Benchmark", + "Product Memory Size (GB)", + "Product HDD Size (GB)", + "Product HDD Text", + "Product HDD Count", + "Product Datacenter", + "Product Network Speed", + "Product Fixed Price", + "Product Next Reduce (seconds)", + "Product Next Reduce Date", + "Addons", + ]); + + for transaction_dyn in transactions { + if let Some(transaction) = transaction_dyn.try_cast::() { + let authorized_keys_table = { + let mut table = Table::new(); + table.add_row(row![b => "Name", "Fingerprint", "Type", "Size"]); + for key in &transaction.authorized_key { + table.add_row(row![ + key.key.name, + key.key.fingerprint, + key.key.key_type, + key.key.size + ]); + } + table + }; + + let host_keys_table = { + let mut table = Table::new(); + table.add_row(row![b => "Fingerprint", "Type", "Size"]); + for key in &transaction.host_key { + table.add_row(row![ + key.key.fingerprint, + key.key.key_type, + key.key.size + ]); + } + table + }; + + table.add_row(row![ + transaction.id, + transaction.date, + transaction.status, + transaction.server_number.map_or("N/A".to_string(), |id| id.to_string()), + transaction.server_ip.as_deref().unwrap_or("N/A"), + transaction.comment.as_deref().unwrap_or("N/A"), + transaction.product.id, + transaction.product.name, + transaction.product.traffic, + transaction.product.dist, + transaction.product.arch, + transaction.product.lang, + transaction.product.cpu, + transaction.product.cpu_benchmark, + transaction.product.memory_size, + transaction.product.hdd_size, + transaction.product.hdd_text, + transaction.product.hdd_count, + transaction.product.datacenter, + transaction.product.network_speed, + transaction.product.fixed_price, + transaction.product.next_reduce, + transaction.product.next_reduce_date, + transaction.addons.join(", "), + ]); + } + } + table.printstd(); } \ No newline at end of file diff --git a/src/scripting/server_ordering.rs b/src/scripting/server_ordering.rs index 2b0d3dc..c7db42c 100644 --- a/src/scripting/server_ordering.rs +++ b/src/scripting/server_ordering.rs @@ -1,4 +1,7 @@ -use crate::api::{Client, models::{OrderServerProduct, Transaction, SshKey}}; +use crate::api::{ + Client, + models::{AuctionServerProduct, AuctionTransaction, OrderServerProduct, SshKey, Transaction}, +}; use rhai::{Array, Dynamic, plugin::*}; pub fn register(engine: &mut Engine) { @@ -9,23 +12,23 @@ pub fn register(engine: &mut Engine) { #[export_module] pub mod server_order_api { - #[rhai_fn(name = "get_server_ordering_product_overview", return_raw)] + #[rhai_fn(name = "get_server_products", return_raw)] pub fn get_server_ordering_product_overview( client: &mut Client, ) -> Result> { let overview_servers = client - .get_server_ordering_product_overview() + .get_server_products() .map_err(|e| Into::>::into(e.to_string()))?; Ok(overview_servers.into_iter().map(Dynamic::from).collect()) } - #[rhai_fn(name = "get_server_ordering_product_by_id", return_raw)] + #[rhai_fn(name = "get_server_product_by_id", return_raw)] pub fn get_server_ordering_product_by_id( client: &mut Client, product_id: &str, ) -> Result> { let product = client - .get_server_ordering_product_by_id(product_id) + .get_server_product_by_id(product_id) .map_err(|e| Into::>::into(e.to_string()))?; Ok(product) } @@ -56,9 +59,14 @@ pub mod server_order_api { let addons = if addons.is_empty() { None } else { - Some(addons.into_iter().map(|a| a.into_string().unwrap()).collect()) + Some( + addons + .into_iter() + .map(|a| a.into_string().unwrap()) + .collect(), + ) }; - + let transaction = client .order_server(product_id, dist, location, authorized_keys, addons) .map_err(|e| Into::>::into(e.to_string()))?; @@ -83,4 +91,94 @@ pub mod server_order_api { .map_err(|e| Into::>::into(e.to_string()))?; Ok(transactions.into_iter().map(Dynamic::from).collect()) } + + #[rhai_fn(name = "get_auction_server_products", return_raw)] + pub fn get_auction_server_products(client: &mut Client) -> Result> { + let products = client + .get_auction_server_products() + .map_err(|e| Into::>::into(e.to_string()))?; + Ok(products.into_iter().map(Dynamic::from).collect()) + } + + #[rhai_fn(name = "get_auction_server_product_by_id", return_raw)] + pub fn get_auction_server_product_by_id( + client: &mut Client, + product_id: &str, + ) -> Result> { + let product = client + .get_auction_server_product_by_id(product_id) + .map_err(|e| Into::>::into(e.to_string()))?; + Ok(product) + } + + #[rhai_fn(name = "get_auction_transactions", return_raw)] + pub fn get_auction_transactions(client: &mut Client) -> Result> { + let transactions = client + .get_auction_transactions() + .map_err(|e| Into::>::into(e.to_string()))?; + Ok(transactions.into_iter().map(Dynamic::from).collect()) + } + + #[rhai_fn(name = "get_auction_transaction_by_id", return_raw)] + pub fn get_auction_transaction_by_id( + client: &mut Client, + transaction_id: &str, + ) -> Result> { + let transaction = client + .get_auction_transaction_by_id(transaction_id) + .map_err(|e| Into::>::into(e.to_string()))?; + Ok(transaction) + } + + #[rhai_fn(name = "order_auction_server", return_raw)] + pub fn order_auction_server( + client: &mut Client, + product_id: i32, + authorized_keys: Array, + dist: Option, + arch: Option, + lang: Option, + comment: Option, + addons: Array, + test: Option, + ) -> Result> { + let authorized_keys: Vec = if authorized_keys.is_empty() { + vec![] + } else if authorized_keys[0].is::() { + authorized_keys + .into_iter() + .map(|k| k.cast::().fingerprint) + .collect() + } else { + authorized_keys + .into_iter() + .map(|k| k.into_string().unwrap()) + .collect() + }; + + let addons = if addons.is_empty() { + None + } else { + Some( + addons + .into_iter() + .map(|a| a.into_string().unwrap()) + .collect(), + ) + }; + + let transaction = client + .order_auction_server( + product_id, + authorized_keys, + dist, + arch, + lang, + comment, + addons, + test, + ) + .map_err(|e| Into::>::into(e.to_string()))?; + Ok(transaction) + } }