implement requirements for ordering server
This commit is contained in:
parent
84aa8cc11d
commit
6cdc468b0a
3
examples/server_ordering.rhai
Normal file
3
examples/server_ordering.rhai
Normal file
@ -0,0 +1,3 @@
|
||||
// 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();
|
@ -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, RescueWrapped, ServerWrapper, SshKeyWrapper,
|
||||
BootWrapper, Cancellation, CancellationWrapper, OrderServerProduct, OrderServerProductWrapper, RescueWrapped, ServerWrapper, SshKeyWrapper
|
||||
};
|
||||
use crate::config::Config;
|
||||
use error::AppError;
|
||||
@ -244,4 +244,16 @@ impl Client {
|
||||
let wrapped: RescueWrapped = self.handle_response(response)?;
|
||||
Ok(wrapped.rescue)
|
||||
}
|
||||
|
||||
pub fn get_server_ordering_product_overview(&self) -> Result<Vec<OrderServerProduct>, AppError> {
|
||||
let response = self
|
||||
.http_client
|
||||
.get(format!("{}/order/server/product", &self.config.api_url))
|
||||
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||
.send()?;
|
||||
|
||||
let wrapped: Vec<OrderServerProductWrapper> = self.handle_response(response)?;
|
||||
let products = wrapped.into_iter().map(|sop| sop.product).collect();
|
||||
Ok(products)
|
||||
}
|
||||
}
|
||||
|
@ -648,4 +648,190 @@ impl From<reqwest::blocking::Response> for ApiError {
|
||||
message: value.text().unwrap_or("The API call returned an error.".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, CustomType)]
|
||||
#[rhai_type(extra = Self::build_rhai_type)]
|
||||
pub struct Price {
|
||||
pub net: String,
|
||||
pub gross: String,
|
||||
pub hourly_net: String,
|
||||
pub hourly_gross: String,
|
||||
}
|
||||
|
||||
impl Price {
|
||||
fn build_rhai_type(builder: &mut TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("Price")
|
||||
.with_get("net", |p: &mut Price| p.net.clone())
|
||||
.with_get("gross", |p: &mut Price| p.gross.clone())
|
||||
.with_get("hourly_net", |p: &mut Price| p.hourly_net.clone())
|
||||
.with_get("hourly_gross", |p: &mut Price| p.hourly_gross.clone())
|
||||
.on_print(|p: &mut Price| p.to_string())
|
||||
.with_fn("pretty_print", |p: &mut Price| p.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Price {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Net: {}, Gross: {}, Hourly Net: {}, Hourly Gross: {}",
|
||||
self.net, self.gross, self.hourly_net, self.hourly_gross
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, CustomType)]
|
||||
#[rhai_type(extra = Self::build_rhai_type)]
|
||||
pub struct PriceSetup {
|
||||
pub net: String,
|
||||
pub gross: String,
|
||||
}
|
||||
|
||||
impl PriceSetup {
|
||||
fn build_rhai_type(builder: &mut TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("PriceSetup")
|
||||
.with_get("net", |p: &mut PriceSetup| p.net.clone())
|
||||
.with_get("gross", |p: &mut PriceSetup| p.gross.clone())
|
||||
.on_print(|p: &mut PriceSetup| p.to_string())
|
||||
.with_fn("pretty_print", |p: &mut PriceSetup| p.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PriceSetup {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Net: {}, Gross: {}", self.net, self.gross)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, CustomType)]
|
||||
#[rhai_type(extra = Self::build_rhai_type)]
|
||||
pub struct ProductPrice {
|
||||
pub location: String,
|
||||
pub price: Price,
|
||||
pub price_setup: PriceSetup,
|
||||
}
|
||||
|
||||
impl ProductPrice {
|
||||
fn build_rhai_type(builder: &mut TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("ProductPrice")
|
||||
.with_get("location", |p: &mut ProductPrice| p.location.clone())
|
||||
.with_get("price", |p: &mut ProductPrice| p.price.clone())
|
||||
.with_get("price_setup", |p: &mut ProductPrice| p.price_setup.clone())
|
||||
.on_print(|p: &mut ProductPrice| p.to_string())
|
||||
.with_fn("pretty_print", |p: &mut ProductPrice| p.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ProductPrice {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Location: {}, Price: ({}), Price Setup: ({})",
|
||||
self.location, self.price, self.price_setup
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, CustomType)]
|
||||
#[rhai_type(extra = Self::build_rhai_type)]
|
||||
pub struct OrderableAddon {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub min: i32,
|
||||
pub max: i32,
|
||||
pub prices: Vec<ProductPrice>,
|
||||
}
|
||||
|
||||
impl OrderableAddon {
|
||||
fn build_rhai_type(builder: &mut TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("OrderableAddon")
|
||||
.with_get("id", |o: &mut OrderableAddon| o.id.clone())
|
||||
.with_get("name", |o: &mut OrderableAddon| o.name.clone())
|
||||
.with_get("min", |o: &mut OrderableAddon| o.min)
|
||||
.with_get("max", |o: &mut OrderableAddon| o.max)
|
||||
.with_get("prices", |o: &mut OrderableAddon| o.prices.clone())
|
||||
.on_print(|o: &mut OrderableAddon| o.to_string())
|
||||
.with_fn("pretty_print", |o: &mut OrderableAddon| o.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OrderableAddon {
|
||||
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!["Name", self.name.clone()]);
|
||||
table.add_row(row!["Min", self.min.to_string()]);
|
||||
table.add_row(row!["Max", self.max.to_string()]);
|
||||
table.add_row(row!["Prices", format!("{:?}", self.prices)]);
|
||||
write!(f, "{}", table)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct OrderServerProductWrapper {
|
||||
pub product: OrderServerProduct,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, CustomType)]
|
||||
#[rhai_type(extra = Self::build_rhai_type)]
|
||||
pub struct OrderServerProduct {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(deserialize_with = "string_or_seq_string")]
|
||||
pub description: Vec<String>,
|
||||
pub traffic: String,
|
||||
#[serde(deserialize_with = "string_or_seq_string")]
|
||||
pub dist: Vec<String>,
|
||||
#[serde(rename = "@deprecated arch", default, deserialize_with = "option_string_or_seq_string")]
|
||||
#[deprecated(note = "use `dist` instead")]
|
||||
pub arch: Option<Vec<String>>,
|
||||
#[serde(deserialize_with = "string_or_seq_string")]
|
||||
pub lang: Vec<String>,
|
||||
#[serde(deserialize_with = "string_or_seq_string")]
|
||||
pub location: Vec<String>,
|
||||
pub prices: Vec<ProductPrice>,
|
||||
pub orderable_addons: Vec<OrderableAddon>,
|
||||
}
|
||||
|
||||
impl OrderServerProduct {
|
||||
fn build_rhai_type(builder: &mut TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("OrderServerProduct")
|
||||
.with_get("id", |o: &mut OrderServerProduct| o.id.clone())
|
||||
.with_get("name", |o: &mut OrderServerProduct| o.name.clone())
|
||||
.with_get("description", |o: &mut OrderServerProduct| o.description.clone())
|
||||
.with_get("traffic", |o: &mut OrderServerProduct| o.traffic.clone())
|
||||
.with_get("dist", |o: &mut OrderServerProduct| o.dist.clone())
|
||||
.with_get("arch", |o: &mut OrderServerProduct| o.arch.clone())
|
||||
.with_get("lang", |o: &mut OrderServerProduct| o.lang.clone())
|
||||
.with_get("location", |o: &mut OrderServerProduct| o.location.clone())
|
||||
.with_get("prices", |o: &mut OrderServerProduct| o.prices.clone())
|
||||
.with_get("orderable_addons", |o: &mut OrderServerProduct| o.orderable_addons.clone())
|
||||
.on_print(|o: &mut OrderServerProduct| o.to_string())
|
||||
.with_fn("pretty_print", |o: &mut OrderServerProduct| o.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OrderServerProduct {
|
||||
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!["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!["Locations", self.location.join(", ")]);
|
||||
table.add_row(row!["Prices", format!("{:?}", self.prices)]);
|
||||
table.add_row(row!["Orderable Addons", format!("{:?}", self.orderable_addons)]);
|
||||
write!(f, "{}", table)
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
use crate::api::Client;
|
||||
use crate::api::models::{Rescue, Linux, Vnc, Windows, Plesk, Cpanel, Boot, Server, SshKey, Cancellation};
|
||||
use crate::api::models::{Rescue, Linux, Vnc, Windows, Plesk, Cpanel, Boot, Server, SshKey, Cancellation, OrderServerProduct};
|
||||
use rhai::{Engine, Scope};
|
||||
|
||||
pub mod server;
|
||||
pub mod ssh_keys;
|
||||
pub mod boot;
|
||||
pub mod printing;
|
||||
pub mod server_ordering;
|
||||
|
||||
pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) {
|
||||
let mut engine = Engine::new();
|
||||
@ -21,11 +22,13 @@ pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) {
|
||||
engine.build_type::<Plesk>();
|
||||
engine.build_type::<Cpanel>();
|
||||
engine.build_type::<Cancellation>();
|
||||
engine.build_type::<OrderServerProduct>();
|
||||
|
||||
server::register(&mut engine);
|
||||
ssh_keys::register(&mut engine);
|
||||
boot::register(&mut engine);
|
||||
printing::register(&mut engine);
|
||||
server_ordering::register(&mut engine);
|
||||
|
||||
scope.push("hetzner", client);
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
use rhai::{Array, Engine};
|
||||
use crate::scripting::{Server, SshKey};
|
||||
use crate::{api::models::OrderServerProduct, scripting::{Server, SshKey}};
|
||||
|
||||
mod servers_table;
|
||||
mod ssh_keys_table;
|
||||
mod server_ordering_table;
|
||||
|
||||
// This will be called when we print(...) or pretty_print() an Array (with Dynamic values)
|
||||
pub fn pretty_print_dispatch(array: Array) {
|
||||
println!("pretty print dispatch");
|
||||
if array.is_empty() {
|
||||
@ -17,6 +19,9 @@ pub fn pretty_print_dispatch(array: Array) {
|
||||
servers_table::pretty_print_servers(array);
|
||||
} else if first.is::<SshKey>() {
|
||||
ssh_keys_table::pretty_print_ssh_keys(array);
|
||||
}
|
||||
else if first.is::<OrderServerProduct>() {
|
||||
server_ordering_table::pretty_print_server_products(array);
|
||||
} else {
|
||||
// Generic fallback for other types
|
||||
for item in array {
|
||||
|
38
src/scripting/printing/server_ordering_table.rs
Normal file
38
src/scripting/printing/server_ordering_table.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use prettytable::{row, Table};
|
||||
use crate::api::models::OrderServerProduct;
|
||||
|
||||
pub fn pretty_print_server_products(products: rhai::Array) {
|
||||
let mut table = Table::new();
|
||||
table.add_row(row![b =>
|
||||
"ID",
|
||||
"Name",
|
||||
"Description",
|
||||
"Traffic",
|
||||
"Location",
|
||||
"Price (Net)",
|
||||
"Price (Gross)",
|
||||
]);
|
||||
|
||||
for product_dyn in products {
|
||||
if let Some(product) = product_dyn.try_cast::<OrderServerProduct>() {
|
||||
let mut price_net = "N/A".to_string();
|
||||
let mut price_gross = "N/A".to_string();
|
||||
|
||||
if let Some(first_price) = product.prices.first() {
|
||||
price_net = first_price.price.net.clone();
|
||||
price_gross = first_price.price.gross.clone();
|
||||
}
|
||||
|
||||
table.add_row(row![
|
||||
product.id,
|
||||
product.name,
|
||||
product.description.join(", "),
|
||||
product.traffic,
|
||||
product.location.join(", "),
|
||||
price_net,
|
||||
price_gross,
|
||||
]);
|
||||
}
|
||||
}
|
||||
table.printstd();
|
||||
}
|
23
src/scripting/server_ordering.rs
Normal file
23
src/scripting/server_ordering.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::api::{Client, models::OrderServerProduct};
|
||||
use rhai::{Array, Dynamic, plugin::*};
|
||||
|
||||
pub fn register(engine: &mut Engine) {
|
||||
let server_order_module = exported_module!(server_order_api);
|
||||
engine.register_global_module(server_order_module.into());
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
pub mod server_order_api {
|
||||
// use super::*;
|
||||
// use rhai::EvalAltResult;
|
||||
|
||||
#[rhai_fn(name = "get_server_ordering_product_overview", return_raw)]
|
||||
pub fn get_server_ordering_product_overview(
|
||||
client: &mut Client,
|
||||
) -> Result<Array, Box<EvalAltResult>> {
|
||||
let overview_servers = client
|
||||
.get_server_ordering_product_overview()
|
||||
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||
Ok(overview_servers.into_iter().map(Dynamic::from).collect())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user