tmp
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
use crate::exchange::Currency;
|
use crate::exchange::{alt_prices, Currency, CurrencyAmount, ExchangeRateService};
|
||||||
use crate::provisioner::PricingEngine;
|
use crate::provisioner::{PricingData, PricingEngine};
|
||||||
use crate::status::VmState;
|
use crate::status::VmState;
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use lnvps_db::{LNVpsDb, Vm, VmCostPlan, VmCustomTemplate, VmHost, VmHostRegion, VmTemplate};
|
use lnvps_db::{LNVpsDb, Vm, VmCostPlan, VmCustomTemplate, VmHost, VmHostRegion, VmTemplate};
|
||||||
@ -136,6 +136,26 @@ pub struct ApiTemplatesResponse {
|
|||||||
pub custom_template: Option<Vec<ApiCustomTemplateParams>>,
|
pub custom_template: Option<Vec<ApiCustomTemplateParams>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ApiTemplatesResponse {
|
||||||
|
pub async fn expand_pricing(&mut self, rates: &Arc<dyn ExchangeRateService>) -> Result<()> {
|
||||||
|
let rates = rates.list_rates().await?;
|
||||||
|
|
||||||
|
for mut template in &mut self.templates {
|
||||||
|
if let Some(list_price) = template.cost_plan.price.first() {
|
||||||
|
for alt_price in alt_prices(
|
||||||
|
&rates,
|
||||||
|
CurrencyAmount(list_price.currency, list_price.amount),
|
||||||
|
) {
|
||||||
|
template.cost_plan.price.push(ApiPrice {
|
||||||
|
currency: alt_price.0,
|
||||||
|
amount: alt_price.1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct ApiCustomTemplateParams {
|
pub struct ApiCustomTemplateParams {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
@ -189,9 +209,9 @@ impl From<ApiCustomVmRequest> for VmCustomTemplate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
#[derive(Copy, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct ApiCustomPrice {
|
pub struct ApiPrice {
|
||||||
pub currency: String,
|
pub currency: Currency,
|
||||||
pub amount: f32,
|
pub amount: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +236,7 @@ impl ApiVmTemplate {
|
|||||||
let template = db.get_vm_template(template_id).await?;
|
let template = db.get_vm_template(template_id).await?;
|
||||||
let cost_plan = db.get_cost_plan(template.cost_plan_id).await?;
|
let cost_plan = db.get_cost_plan(template.cost_plan_id).await?;
|
||||||
let region = db.get_host_region(template.region_id).await?;
|
let region = db.get_host_region(template.region_id).await?;
|
||||||
Ok(Self::from_standard_data(&template, &cost_plan, ®ion))
|
Self::from_standard_data(&template, &cost_plan, ®ion)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_custom(db: &Arc<dyn LNVpsDb>, vm_id: u64, template_id: u64) -> Result<Self> {
|
pub async fn from_custom(db: &Arc<dyn LNVpsDb>, vm_id: u64, template_id: u64) -> Result<Self> {
|
||||||
@ -239,6 +259,7 @@ impl ApiVmTemplate {
|
|||||||
name: pricing.name,
|
name: pricing.name,
|
||||||
amount: price.total(),
|
amount: price.total(),
|
||||||
currency: price.currency,
|
currency: price.currency,
|
||||||
|
other_price: vec![], // filled externally
|
||||||
interval_amount: 1,
|
interval_amount: 1,
|
||||||
interval_type: ApiVmCostPlanIntervalType::Month,
|
interval_type: ApiVmCostPlanIntervalType::Month,
|
||||||
},
|
},
|
||||||
@ -263,8 +284,8 @@ impl ApiVmTemplate {
|
|||||||
template: &VmTemplate,
|
template: &VmTemplate,
|
||||||
cost_plan: &VmCostPlan,
|
cost_plan: &VmCostPlan,
|
||||||
region: &VmHostRegion,
|
region: &VmHostRegion,
|
||||||
) -> Self {
|
) -> Result<Self> {
|
||||||
Self {
|
Ok(Self {
|
||||||
id: template.id,
|
id: template.id,
|
||||||
name: template.name.clone(),
|
name: template.name.clone(),
|
||||||
created: template.created,
|
created: template.created,
|
||||||
@ -278,7 +299,9 @@ impl ApiVmTemplate {
|
|||||||
id: cost_plan.id,
|
id: cost_plan.id,
|
||||||
name: cost_plan.name.clone(),
|
name: cost_plan.name.clone(),
|
||||||
amount: cost_plan.amount,
|
amount: cost_plan.amount,
|
||||||
currency: cost_plan.currency.clone(),
|
currency: Currency::from_str(&cost_plan.currency)
|
||||||
|
.map_err(|_| anyhow!("Invalid currency: {}", &cost_plan.currency))?,
|
||||||
|
other_price: vec![], //filled externally
|
||||||
interval_amount: cost_plan.interval_amount,
|
interval_amount: cost_plan.interval_amount,
|
||||||
interval_type: cost_plan.interval_type.clone().into(),
|
interval_type: cost_plan.interval_type.clone().into(),
|
||||||
},
|
},
|
||||||
@ -286,7 +309,7 @@ impl ApiVmTemplate {
|
|||||||
id: region.id,
|
id: region.id,
|
||||||
name: region.name.clone(),
|
name: region.name.clone(),
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
@ -311,8 +334,9 @@ impl From<lnvps_db::VmCostPlanIntervalType> for ApiVmCostPlanIntervalType {
|
|||||||
pub struct ApiVmCostPlan {
|
pub struct ApiVmCostPlan {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub currency: Currency,
|
||||||
pub amount: f32,
|
pub amount: f32,
|
||||||
pub currency: String,
|
pub other_price: Vec<ApiPrice>,
|
||||||
pub interval_amount: u64,
|
pub interval_amount: u64,
|
||||||
pub interval_type: ApiVmCostPlanIntervalType,
|
pub interval_type: ApiVmCostPlanIntervalType,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::api::model::{
|
use crate::api::model::{
|
||||||
AccountPatchRequest, ApiCustomPrice, ApiCustomTemplateDiskParam, ApiCustomTemplateParams,
|
AccountPatchRequest, ApiPrice, ApiCustomTemplateDiskParam, ApiCustomTemplateParams,
|
||||||
ApiCustomVmOrder, ApiCustomVmRequest, ApiTemplatesResponse, ApiUserSshKey, ApiVmHostRegion,
|
ApiCustomVmOrder, ApiCustomVmRequest, ApiTemplatesResponse, ApiUserSshKey, ApiVmHostRegion,
|
||||||
ApiVmIpAssignment, ApiVmOsImage, ApiVmPayment, ApiVmStatus, ApiVmTemplate, CreateSshKey,
|
ApiVmIpAssignment, ApiVmOsImage, ApiVmPayment, ApiVmStatus, ApiVmTemplate, CreateSshKey,
|
||||||
CreateVmRequest, VMPatchRequest,
|
CreateVmRequest, VMPatchRequest,
|
||||||
@ -301,7 +301,7 @@ async fn v1_list_vm_templates(db: &State<Arc<dyn LNVpsDb>>) -> ApiResult<ApiTemp
|
|||||||
.filter_map(|i| {
|
.filter_map(|i| {
|
||||||
let cp = cost_plans.get(&i.cost_plan_id)?;
|
let cp = cost_plans.get(&i.cost_plan_id)?;
|
||||||
let hr = regions.get(&i.region_id)?;
|
let hr = regions.get(&i.region_id)?;
|
||||||
Some(ApiVmTemplate::from_standard_data(i, cp, hr))
|
ApiVmTemplate::from_standard_data(i, cp, hr).ok()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let custom_templates: Vec<VmCustomPricing> =
|
let custom_templates: Vec<VmCustomPricing> =
|
||||||
@ -373,12 +373,12 @@ async fn v1_list_vm_templates(db: &State<Arc<dyn LNVpsDb>>) -> ApiResult<ApiTemp
|
|||||||
async fn v1_custom_template_calc(
|
async fn v1_custom_template_calc(
|
||||||
db: &State<Arc<dyn LNVpsDb>>,
|
db: &State<Arc<dyn LNVpsDb>>,
|
||||||
req: Json<ApiCustomVmRequest>,
|
req: Json<ApiCustomVmRequest>,
|
||||||
) -> ApiResult<ApiCustomPrice> {
|
) -> ApiResult<ApiPrice> {
|
||||||
// create a fake template from the request to generate the price
|
// create a fake template from the request to generate the price
|
||||||
let template: VmCustomTemplate = req.0.into();
|
let template: VmCustomTemplate = req.0.into();
|
||||||
|
|
||||||
let price = PricingEngine::get_custom_vm_cost_amount(db, 0, &template).await?;
|
let price = PricingEngine::get_custom_vm_cost_amount(db, 0, &template).await?;
|
||||||
ApiData::ok(ApiCustomPrice {
|
ApiData::ok(ApiPrice {
|
||||||
currency: price.currency.clone(),
|
currency: price.currency.clone(),
|
||||||
amount: price.total(),
|
amount: price.total(),
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use anyhow::{Error, Result};
|
use anyhow::{anyhow, ensure, Context, Error, Result};
|
||||||
use lnvps_db::async_trait;
|
use lnvps_db::async_trait;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rocket::serde::Deserialize;
|
use rocket::serde::Deserialize;
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
@ -9,7 +10,7 @@ use std::str::FromStr;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Copy, JsonSchema)]
|
||||||
pub enum Currency {
|
pub enum Currency {
|
||||||
EUR,
|
EUR,
|
||||||
BTC,
|
BTC,
|
||||||
@ -39,12 +40,12 @@ impl FromStr for Currency {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Ticker(Currency, Currency);
|
pub struct Ticker(pub Currency, pub Currency);
|
||||||
|
|
||||||
impl Ticker {
|
impl Ticker {
|
||||||
pub fn btc_rate(cur: &str) -> Result<Self> {
|
pub fn btc_rate(cur: &str) -> Result<Self> {
|
||||||
let to_cur: Currency = cur.parse().map_err(|_| Error::msg(""))?;
|
let to_cur: Currency = cur.parse().map_err(|_| anyhow!("Invalid currency"))?;
|
||||||
Ok(Ticker(Currency::BTC, to_cur))
|
Ok(Ticker(Currency::BTC, to_cur))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,11 +59,43 @@ impl Display for Ticker {
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct TickerRate(pub Ticker, pub f32);
|
pub struct TickerRate(pub Ticker, pub f32);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct CurrencyAmount(pub Currency, pub f32);
|
||||||
|
|
||||||
|
impl TickerRate {
|
||||||
|
pub fn can_convert(&self, currency: Currency) -> bool {
|
||||||
|
currency == self.0 .0 || currency == self.0 .1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert from the source currency into the target currency
|
||||||
|
pub fn convert(&self, source: CurrencyAmount) -> Result<CurrencyAmount> {
|
||||||
|
ensure!(
|
||||||
|
self.can_convert(source.0),
|
||||||
|
"Cant convert, currency doesnt match"
|
||||||
|
);
|
||||||
|
if source.0 == self.0 .0 {
|
||||||
|
Ok(CurrencyAmount(self.0 .1, source.1 * self.1))
|
||||||
|
} else {
|
||||||
|
Ok(CurrencyAmount(self.0 .0, source.1 / self.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ExchangeRateService: Send + Sync {
|
pub trait ExchangeRateService: Send + Sync {
|
||||||
async fn fetch_rates(&self) -> Result<Vec<TickerRate>>;
|
async fn fetch_rates(&self) -> Result<Vec<TickerRate>>;
|
||||||
async fn set_rate(&self, ticker: Ticker, amount: f32);
|
async fn set_rate(&self, ticker: Ticker, amount: f32);
|
||||||
async fn get_rate(&self, ticker: Ticker) -> Option<f32>;
|
async fn get_rate(&self, ticker: Ticker) -> Option<f32>;
|
||||||
|
async fn list_rates(&self) -> Result<Vec<TickerRate>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get alternative prices based on a source price
|
||||||
|
pub fn alt_prices(rates: &Vec<TickerRate>, source: CurrencyAmount) -> Vec<CurrencyAmount> {
|
||||||
|
// TODO: return all alt prices by cross-converting all currencies
|
||||||
|
rates
|
||||||
|
.iter()
|
||||||
|
.filter_map(|r| r.convert(source).ok())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
@ -100,6 +133,11 @@ impl ExchangeRateService for DefaultRateCache {
|
|||||||
let cache = self.cache.read().await;
|
let cache = self.cache.read().await;
|
||||||
cache.get(&ticker).cloned()
|
cache.get(&ticker).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn list_rates(&self) -> Result<Vec<TickerRate>> {
|
||||||
|
let cache = self.cache.read().await;
|
||||||
|
Ok(cache.iter().map(|(k, v)| TickerRate(*k, *v)).collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -109,3 +147,27 @@ struct MempoolRates {
|
|||||||
#[serde(rename = "EUR")]
|
#[serde(rename = "EUR")]
|
||||||
pub eur: Option<f32>,
|
pub eur: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const RATE: f32 = 95_000.0;
|
||||||
|
#[test]
|
||||||
|
fn convert() {
|
||||||
|
let ticker = Ticker::btc_rate("EUR").unwrap();
|
||||||
|
let f = TickerRate(ticker, RATE);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
f.convert(CurrencyAmount(Currency::EUR, 5.0)).unwrap(),
|
||||||
|
CurrencyAmount(Currency::BTC, 5.0 / RATE)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
f.convert(CurrencyAmount(Currency::BTC, 0.001)).unwrap(),
|
||||||
|
CurrencyAmount(Currency::EUR, RATE * 0.001)
|
||||||
|
);
|
||||||
|
assert!(!f.can_convert(Currency::USD));
|
||||||
|
assert!(f.can_convert(Currency::EUR));
|
||||||
|
assert!(f.can_convert(Currency::BTC));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
22
src/mocks.rs
22
src/mocks.rs
@ -80,7 +80,7 @@ impl MockDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mock_vm() ->Vm {
|
pub fn mock_vm() -> Vm {
|
||||||
let template = Self::mock_template();
|
let template = Self::mock_template();
|
||||||
Vm {
|
Vm {
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -861,13 +861,13 @@ impl DnsServer for MockDnsServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct MockExchangeRate {
|
pub struct MockExchangeRate {
|
||||||
pub rate: Arc<Mutex<f32>>,
|
pub rate: Arc<Mutex<HashMap<Ticker, f32>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockExchangeRate {
|
impl MockExchangeRate {
|
||||||
pub fn new(rate: f32) -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
rate: Arc::new(Mutex::new(rate)),
|
rate: Arc::new(Mutex::new(Default::default())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -876,16 +876,24 @@ impl MockExchangeRate {
|
|||||||
impl ExchangeRateService for MockExchangeRate {
|
impl ExchangeRateService for MockExchangeRate {
|
||||||
async fn fetch_rates(&self) -> anyhow::Result<Vec<TickerRate>> {
|
async fn fetch_rates(&self) -> anyhow::Result<Vec<TickerRate>> {
|
||||||
let r = self.rate.lock().await;
|
let r = self.rate.lock().await;
|
||||||
Ok(vec![TickerRate(Ticker::btc_rate("EUR")?, *r)])
|
Ok(r.iter().map(|(k, v)| TickerRate(k.clone(), *v)).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_rate(&self, ticker: Ticker, amount: f32) {
|
async fn set_rate(&self, ticker: Ticker, amount: f32) {
|
||||||
let mut r = self.rate.lock().await;
|
let mut r = self.rate.lock().await;
|
||||||
*r = amount;
|
if let Some(v) = r.get_mut(&ticker) {
|
||||||
|
*v += amount;
|
||||||
|
} else {
|
||||||
|
r.insert(ticker, amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_rate(&self, ticker: Ticker) -> Option<f32> {
|
async fn get_rate(&self, ticker: Ticker) -> Option<f32> {
|
||||||
let r = self.rate.lock().await;
|
let r = self.rate.lock().await;
|
||||||
Some(*r)
|
r.get(&ticker).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_rates(&self) -> anyhow::Result<Vec<TickerRate>> {
|
||||||
|
self.fetch_rates().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::exchange::{ExchangeRateService, Ticker};
|
use crate::exchange::{Currency, ExchangeRateService, Ticker};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use chrono::{DateTime, Days, Months, TimeDelta, Utc};
|
use chrono::{DateTime, Days, Months, TimeDelta, Utc};
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use lnvps_db::{LNVpsDb, Vm, VmCostPlan, VmCostPlanIntervalType, VmCustomTemplate, VmPayment};
|
use lnvps_db::{LNVpsDb, Vm, VmCostPlan, VmCostPlanIntervalType, VmCustomTemplate, VmPayment};
|
||||||
@ -86,8 +86,13 @@ impl PricingEngine {
|
|||||||
let ip4_cost = pricing.ip4_cost * v4s as f32;
|
let ip4_cost = pricing.ip4_cost * v4s as f32;
|
||||||
let ip6_cost = pricing.ip6_cost * v6s as f32;
|
let ip6_cost = pricing.ip6_cost * v6s as f32;
|
||||||
|
|
||||||
|
let currency: Currency = if let Ok(p) = pricing.currency.parse() {
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
bail!("Invalid currency")
|
||||||
|
};
|
||||||
Ok(PricingData {
|
Ok(PricingData {
|
||||||
currency: pricing.currency,
|
currency,
|
||||||
cpu_cost,
|
cpu_cost,
|
||||||
memory_cost,
|
memory_cost,
|
||||||
ip6_cost,
|
ip6_cost,
|
||||||
@ -109,9 +114,7 @@ impl PricingEngine {
|
|||||||
|
|
||||||
// custom templates are always 1-month intervals
|
// custom templates are always 1-month intervals
|
||||||
let time_value = (vm.expires.add(Months::new(1)) - vm.expires).num_seconds() as u64;
|
let time_value = (vm.expires.add(Months::new(1)) - vm.expires).num_seconds() as u64;
|
||||||
let (cost_msats, rate) = self
|
let (cost_msats, rate) = self.get_msats_amount(price.currency, price.total()).await?;
|
||||||
.get_msats_amount(&price.currency, price.total())
|
|
||||||
.await?;
|
|
||||||
Ok(CostResult::New {
|
Ok(CostResult::New {
|
||||||
msats: cost_msats,
|
msats: cost_msats,
|
||||||
rate,
|
rate,
|
||||||
@ -120,8 +123,8 @@ impl PricingEngine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_msats_amount(&self, currency: &str, amount: f32) -> Result<(u64, f32)> {
|
async fn get_msats_amount(&self, currency: Currency, amount: f32) -> Result<(u64, f32)> {
|
||||||
let ticker = Ticker::btc_rate(¤cy)?;
|
let ticker = Ticker(Currency::BTC, currency);
|
||||||
let rate = if let Some(r) = self.rates.get_rate(ticker).await {
|
let rate = if let Some(r) = self.rates.get_rate(ticker).await {
|
||||||
r
|
r
|
||||||
} else {
|
} else {
|
||||||
@ -157,7 +160,10 @@ impl PricingEngine {
|
|||||||
let cost_plan = self.db.get_cost_plan(template.cost_plan_id).await?;
|
let cost_plan = self.db.get_cost_plan(template.cost_plan_id).await?;
|
||||||
|
|
||||||
let (cost_msats, rate) = self
|
let (cost_msats, rate) = self
|
||||||
.get_msats_amount(cost_plan.currency.as_str(), cost_plan.amount)
|
.get_msats_amount(
|
||||||
|
cost_plan.currency.parse().expect("Invalid currency"),
|
||||||
|
cost_plan.amount,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let time_value = Self::next_template_expire(&vm, &cost_plan);
|
let time_value = Self::next_template_expire(&vm, &cost_plan);
|
||||||
Ok(CostResult::New {
|
Ok(CostResult::New {
|
||||||
@ -188,7 +194,7 @@ pub enum CostResult {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PricingData {
|
pub struct PricingData {
|
||||||
pub currency: String,
|
pub currency: Currency,
|
||||||
pub cpu_cost: f32,
|
pub cpu_cost: f32,
|
||||||
pub memory_cost: f32,
|
pub memory_cost: f32,
|
||||||
pub ip4_cost: f32,
|
pub ip4_cost: f32,
|
||||||
@ -274,7 +280,9 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn standard_pricing() -> Result<()> {
|
async fn standard_pricing() -> Result<()> {
|
||||||
let db = MockDb::default();
|
let db = MockDb::default();
|
||||||
let rates = Arc::new(MockExchangeRate::new(MOCK_RATE));
|
let rates = Arc::new(MockExchangeRate::new());
|
||||||
|
rates.set_rate(Ticker::btc_rate("EUR")?, MOCK_RATE).await;
|
||||||
|
|
||||||
// add basic vm
|
// add basic vm
|
||||||
{
|
{
|
||||||
let mut v = db.vms.lock().await;
|
let mut v = db.vms.lock().await;
|
||||||
|
Reference in New Issue
Block a user