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 self::models::{Boot, Rescue, Server, SshKey};
|
||||||
use crate::api::error::ApiError;
|
use crate::api::error::ApiError;
|
||||||
use crate::api::models::{
|
use crate::api::models::{
|
||||||
BootWrapper, Cancellation, CancellationWrapper, RescueWrapped, ServerWrapper, SshKeyWrapper,
|
BootWrapper, Cancellation, CancellationWrapper, OrderServerProduct, OrderServerProductWrapper, RescueWrapped, ServerWrapper, SshKeyWrapper
|
||||||
};
|
};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use error::AppError;
|
use error::AppError;
|
||||||
@ -244,4 +244,16 @@ impl Client {
|
|||||||
let wrapped: RescueWrapped = self.handle_response(response)?;
|
let wrapped: RescueWrapped = self.handle_response(response)?;
|
||||||
Ok(wrapped.rescue)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -649,3 +649,189 @@ impl From<reqwest::blocking::Response> for ApiError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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::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};
|
use rhai::{Engine, Scope};
|
||||||
|
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod ssh_keys;
|
pub mod ssh_keys;
|
||||||
pub mod boot;
|
pub mod boot;
|
||||||
pub mod printing;
|
pub mod printing;
|
||||||
|
pub mod server_ordering;
|
||||||
|
|
||||||
pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) {
|
pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) {
|
||||||
let mut engine = Engine::new();
|
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::<Plesk>();
|
||||||
engine.build_type::<Cpanel>();
|
engine.build_type::<Cpanel>();
|
||||||
engine.build_type::<Cancellation>();
|
engine.build_type::<Cancellation>();
|
||||||
|
engine.build_type::<OrderServerProduct>();
|
||||||
|
|
||||||
server::register(&mut engine);
|
server::register(&mut engine);
|
||||||
ssh_keys::register(&mut engine);
|
ssh_keys::register(&mut engine);
|
||||||
boot::register(&mut engine);
|
boot::register(&mut engine);
|
||||||
printing::register(&mut engine);
|
printing::register(&mut engine);
|
||||||
|
server_ordering::register(&mut engine);
|
||||||
|
|
||||||
scope.push("hetzner", client);
|
scope.push("hetzner", client);
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use rhai::{Array, Engine};
|
use rhai::{Array, Engine};
|
||||||
use crate::scripting::{Server, SshKey};
|
use crate::{api::models::OrderServerProduct, scripting::{Server, SshKey}};
|
||||||
|
|
||||||
mod servers_table;
|
mod servers_table;
|
||||||
mod ssh_keys_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) {
|
pub fn pretty_print_dispatch(array: Array) {
|
||||||
println!("pretty print dispatch");
|
println!("pretty print dispatch");
|
||||||
if array.is_empty() {
|
if array.is_empty() {
|
||||||
@ -17,6 +19,9 @@ pub fn pretty_print_dispatch(array: Array) {
|
|||||||
servers_table::pretty_print_servers(array);
|
servers_table::pretty_print_servers(array);
|
||||||
} else if first.is::<SshKey>() {
|
} else if first.is::<SshKey>() {
|
||||||
ssh_keys_table::pretty_print_ssh_keys(array);
|
ssh_keys_table::pretty_print_ssh_keys(array);
|
||||||
|
}
|
||||||
|
else if first.is::<OrderServerProduct>() {
|
||||||
|
server_ordering_table::pretty_print_server_products(array);
|
||||||
} else {
|
} else {
|
||||||
// Generic fallback for other types
|
// Generic fallback for other types
|
||||||
for item in array {
|
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