Compare commits

..

No commits in common. "0bd9b55f87d181511e71c6aa00428c3c5ebe518e" and "efb00dcf5e9dfb043af7e37f22b2bb45b82ed48e" have entirely different histories.

12 changed files with 107 additions and 329 deletions

7
.env
View File

@ -1,8 +1,3 @@
CONFIG_SERVER_CREDS = "ws://127.0.0.1:8080" CONFIG_SERVER_CREDS = "ws://127.0.0.1:8080"
API_GRUBBER_SOCKET = "api-grub.sock" API_GRUBBER_SOCKET = "api-grub.sock"
PREPROC_SOCKET = "preproc.sock" PREPROC_SOCKET = "preproc.sock"
DB_HOST = "192.168.2.37"
DB_USER = "vlad"
DB_PASSWORD = "vlad"
DB_DBNAME = "vks"

View File

@ -1,31 +1,13 @@
{ {
"id" : 1 , "endpoints" : [
"template" : {
[{ "url" : "http://127.0.0.1:8081/ping",
"id" :"mock_api_1", "method" : "GET"
"name" : "Mock / ", },
"url" : "http://127.0.0.1:8081/", {
"method" : "GET", "url" : "http://127.0.0.1:8081/",
"measure" : "method" : "GET"
[ }
"operation", "response" ],
] "delay" : 5
}, }
{
"id" :"mock_api_2",
"name" : "Mock /ping ",
"url" : "http://127.0.0.1:8081/ping",
"method" : "GET",
"measure" :
[
"operation", "response", "empty_field"
]
}
],
"ip_address" : "127.0.0.1:8081",
"login" : "",
"pass" : "" ,
"api_key" : "908c709827bd40n98r7209837x98273",
"period" : 10,
"timeout" : 10
}

View File

@ -12,7 +12,4 @@ env_logger = "0.11.6"
log = "0.4.25" log = "0.4.25"
anyhow = "1.0.95" anyhow = "1.0.95"
chrono = "0.4.39" chrono = "0.4.39"
reqwest = { version = "0.12.12", features = ["rustls-tls", "json"] } reqwest = { version = "0.12.12", features = ["rustls-tls", "json"] }
deadpool-postgres = "0.14.1"
tokio-postgres = "0.7.12"
dotenv = "0.15.0"

View File

@ -1,7 +1,7 @@
// mod to communicate with api-grub config file // mod to communicate with api-grub config file
// 1) check changes in unix-socket // 1) check changes in unix-socket
// 2) save changes in local config file // 2) save changes in local config file
use integr_structs::api::{ApiConfig, ApiConfigV2}; use integr_structs::api::ApiConfig;
use anyhow::{Error, Ok, Result}; use anyhow::{Error, Ok, Result};
use log::{info, warn, error}; use log::{info, warn, error};
use std::{fs, path::Path}; use std::{fs, path::Path};
@ -15,7 +15,7 @@ const CONFIG_PATH: &str = "config_api.json";
const SOCKET_PATH: &str = "api-grub.sock"; const SOCKET_PATH: &str = "api-grub.sock";
// todo! rewrite to use current_exe // todo! rewrite to use current_exe
pub async fn pull_local_config() -> Result<ApiConfigV2> { pub async fn pull_local_config() -> Result<ApiConfig> {
// let conf_path = std::env::current_exe()?; // let conf_path = std::env::current_exe()?;
let path = Path::new(CONFIG_PATH); let path = Path::new(CONFIG_PATH);
// return match conf_path.parent() { // return match conf_path.parent() {
@ -28,7 +28,7 @@ pub async fn pull_local_config() -> Result<ApiConfigV2> {
// None => Err(Error::msg("No local conf was found")) // None => Err(Error::msg("No local conf was found"))
// } // }
if path.exists() && path.is_file() { if path.exists() && path.is_file() {
let config: ApiConfigV2 = from_str( let config: ApiConfig = from_str(
&fs::read_to_string(CONFIG_PATH)? &fs::read_to_string(CONFIG_PATH)?
)?; )?;
Ok(config) Ok(config)
@ -39,7 +39,7 @@ pub async fn pull_local_config() -> Result<ApiConfigV2> {
// for config pulling // for config pulling
// ++++ reader to channel // ++++ reader to channel
pub async fn init_config_grub_mechanism(tx: &Sender<ApiConfigV2>) -> Result<()> { pub async fn init_config_grub_mechanism(tx: &Sender<ApiConfig>) -> Result<()> {
info!("Initializing Unix-Socket listening for pulling new configs..."); info!("Initializing Unix-Socket listening for pulling new configs...");
let server = init_unix_listener().await?; let server = init_unix_listener().await?;
// //
@ -51,7 +51,7 @@ pub async fn init_config_grub_mechanism(tx: &Sender<ApiConfigV2>) -> Result<()>
if let Err(er) = stream.read_to_string(&mut buffer).await { if let Err(er) = stream.read_to_string(&mut buffer).await {
warn!("Cannot read config from stream due to {}", er); warn!("Cannot read config from stream due to {}", er);
} else { } else {
let config: Result<ApiConfigV2, serde_json::Error> = from_str(&buffer); let config: Result<ApiConfig, serde_json::Error> = from_str(&buffer);
if let stdOk(conf) = config { if let stdOk(conf) = config {
info!("New config was pulled from Unix-Stream. Saving it locally and sharing with API-grub module..."); info!("New config was pulled from Unix-Stream. Saving it locally and sharing with API-grub module...");
if let Err(er) = save_new_config(&buffer).await { if let Err(er) = save_new_config(&buffer).await {
@ -97,13 +97,13 @@ mod config_unittests {
#[test] #[test]
async fn check_save_new_config() { async fn check_save_new_config() {
use std::fs; use std::fs;
use integr_structs::api::ApiConfigV2; use integr_structs::api::ApiConfig;
use serde_json::to_string; use serde_json::to_string;
let test_config_path = "test_config_api.json"; let test_config_path = "test_config_api.json";
// config gen // config gen
let config = to_string::<ApiConfigV2>(&ApiConfigV2::default()); let config = to_string::<ApiConfig>(&ApiConfig::default());
assert!(config.is_ok()); assert!(config.is_ok());
let config = config.unwrap(); let config = config.unwrap();

View File

@ -3,7 +3,7 @@ mod net;
mod logger; mod logger;
use anyhow::Result; use anyhow::Result;
use integr_structs::api::ApiConfigV2; use integr_structs::api::ApiConfig;
use logger::setup_logger; use logger::setup_logger;
use log::{info, warn}; use log::{info, warn};
use config::{pull_local_config, init_config_grub_mechanism}; use config::{pull_local_config, init_config_grub_mechanism};
@ -19,7 +19,7 @@ async fn main() -> Result<()>{
setup_logger().await?; setup_logger().await?;
let config = get_config().await; let config = get_config().await;
// config update channel // config update channel
let (tx, mut rx) = mpsc::channel::<ApiConfigV2>(1); let (tx, mut rx) = mpsc::channel::<ApiConfig>(1);
// futures // futures
// todo : rewrite with spawn // todo : rewrite with spawn
let config_fut = init_config_grub_mechanism(&tx); let config_fut = init_config_grub_mechanism(&tx);
@ -30,7 +30,7 @@ async fn main() -> Result<()>{
Ok(()) Ok(())
} }
async fn get_config() -> ApiConfigV2 { async fn get_config() -> ApiConfig {
return match pull_local_config().await { return match pull_local_config().await {
Ok(conf) => { Ok(conf) => {
info!("Local config was loaded"); info!("Local config was loaded");
@ -38,7 +38,7 @@ async fn get_config() -> ApiConfigV2 {
}, },
Err(er) => { Err(er) => {
warn!("Cannot get local config due to {}", er); warn!("Cannot get local config due to {}", er);
ApiConfigV2::default() ApiConfig::default()
} }
} }
} }

View File

@ -1,70 +1,10 @@
// module to handle unix-socket connection + pulling info from api // module to handle unix-socket connection + pulling info from api
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use integr_structs::api::{ApiConfigV2, ProcessedEndpoint}; use integr_structs::api::ApiConfig;
use log::{error, info}; use log::{error, info};
use serde_json::Value;
use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Receiver;
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
use reqwest::{Client, Method}; use reqwest::{Client, Method};
use std::sync::Arc;
use tokio::task::JoinHandle;
// use tokio::sync::Mutex;
use tokio_postgres::types::ToSql;
use deadpool_postgres::{Config, Pool, Runtime, Client as PgClient};
use tokio_postgres::NoTls;
use dotenv::dotenv;
use std::env;
// type BufferType = Arc<Mutex<Vec<String>>>;
struct Exporter {
pool : Option<Pool>,
}
impl Exporter {
fn config_construct() -> Result<Config> {
let mut cfg = Config::new();
cfg.host = Some(env::var("DB_HOST")?);
cfg.dbname = Some(env::var("DB_DBNAME")?);
cfg.user = Some(env::var("DB_USER")?);
cfg.password = Some(env::var("DB_PASSWORD")?);
Ok(cfg)
}
fn pool_construct() -> Option<Pool> {
return match Self::config_construct() {
Ok(config) => {
if let Ok(pool) = config.create_pool(Some(Runtime::Tokio1), NoTls) {
info!("Connected to PostgreSQL");
return Some(pool);
}
None
},
Err(_) => {
error!("Bad DB credentials or it's unreachable");
None
},
}
}
pub fn is_no_connection(&self) -> bool { self.pool.is_none() }
pub fn init() -> Self {
Self {
pool : Self::pool_construct()
}
}
pub async fn get_connection_from_pool(&self) -> Option<PgClient> {
if let Some(pool) = &self.pool {
return Some(pool.get().await.ok()?);
}
None
}
pub async fn export_data(client: PgClient, metrics: &str) -> Result<()> {
// client.
let query = "INSERT INTO metrics (body) VALUES ($1);";
let _ = client.execute(query, &[&query]).await?;
Ok(())
}
}
struct RestMethod; struct RestMethod;
@ -75,113 +15,69 @@ impl RestMethod {
"patch" => Method::PATCH, "patch" => Method::PATCH,
"put" => Method::PUT, "put" => Method::PUT,
"delete" => Method::DELETE, "delete" => Method::DELETE,
"head" => Method::HEAD,
"trace" => Method::TRACE,
"options" => Method::OPTIONS,
"connect" => Method::CONNECT,
"get" | _ => Method::GET "get" | _ => Method::GET
} }
} }
} }
struct ApiPoll<'a> { struct ApiPoll<'a> {
config : &'a mut ApiConfigV2, config : &'a mut ApiConfig,
client : Client, client : Client,
} }
impl<'a> ApiPoll<'a> { impl<'a> ApiPoll<'a> {
pub async fn new(poll_cfg : &'a mut ApiConfigV2) -> Self { pub async fn new(poll_cfg : &'a mut ApiConfig) -> Self {
Self { Self {
config : poll_cfg, config : poll_cfg,
client : Client::new(), client : Client::new(),
} }
} }
// can be weak and with bug test needed // can be weak and with bug test needed
pub async fn change_config(&mut self, conf: ApiConfigV2) { pub async fn change_config(&mut self, conf: ApiConfig) {
*self.config = conf; *self.config = conf;
} }
pub async fn is_default(&self) -> bool { pub async fn is_default(&self) -> bool {
self.config.template.len() == 0 self.config.endpoints.len() == 0
} }
pub async fn process_polling(&self, exporter: Arc<Exporter>) -> Result<()> { pub async fn process_polling(&self) -> Result<Vec<String>> {
// let buffer: BufferType = Arc::new(Mutex::new(vec![])); let mut buffer: Vec<String> = vec![];
let mut join_handles: Vec<JoinHandle<Result<()>>> = vec![];
let client = Arc::new(self.client.clone());
let template = Arc::new(self.config.template.clone());
// TODO: rewrite nextly to async // TODO: rewrite nextly to async
for point in template.iter() { for point in &self.config.endpoints {
let point = Arc::new(point.clone()); // let a = self.client.get(&point.url).send().await.unwrap();
// let buffer = buffer.clone(); // a.text().await.unwrap();
let client = client.clone(); match self.client.request(RestMethod::from_str(&point.method).await, &point.url).send().await {
let exporter = exporter.clone(); Ok(resp) => {
let endpoint_processer = tokio::spawn(async move { if !resp.status().is_success() {
let point = point.clone(); error!("ErrorCode in Response from API. Check configuration");
match client.request(RestMethod::from_str(&point.method).await, &point.url).send().await { continue;
Ok(resp) => { }
if !resp.status().is_success() { if let Ok(text) = resp.text().await {
error!("ErrorCode in Response from API. Check configuration"); info!("{}: {} - Successfull grubbing info", &point.method.to_uppercase(), &point.url);
return Err(Error::msg("Error during sending request")); buffer.push(text);
} } else {
if let Ok(text) = resp.text().await { error!("{}: {} - Error with extracting text field from Response", &point.method.to_uppercase(), &point.url);
// }
let metrics = ProcessedEndpoint::from_target_response(&text, &point)?; },
// Err(er) => {
if let Some(conn) = exporter.get_connection_from_pool().await { error!("{}: {} - Query crushed due to {}", &point.method.to_uppercase(), &point.url, er);
if let Err(er) = Exporter::export_data(conn, &metrics).await { },
error!("Cannot export data to DB during to: {}", er); }
return Err(Error::msg("Error during exporting data to DB"));
}
} else {
if !exporter.is_no_connection() {
return Err(Error::msg("Error during getting connection from pool"));
}
}
// let mut buffer = buffer.lock().await;
// buffer.push(text);
} else {
error!("{}: {} - Error with extracting text field from Response", &point.method.to_uppercase(), &point.url);
}
},
Err(_) => {
error!("{}: {} endpoint is unreachable", &point.method.to_uppercase(), &point.url);
},
}
Ok(())
});
join_handles.push(endpoint_processer);
} }
match &buffer.len() {
for i in join_handles { 0 => Err(Error::msg("Error due to API grubbing. Check config" )),
let _ = i.await; _ => Ok(buffer),
} }
// let buffer = buffer.lock().await;
// match &buffer.len() {
// 0 => Err(Error::msg("Error due to API grubbing. Check config" )),
// _ => {
// Ok(())
// },
// }
Ok(())
} }
pub async fn get_delay(&self) -> u32 { pub async fn get_delay(&self) -> u32 {
self.config.timeout self.config.delay
} }
} }
// for api info pulling // for api info pulling
pub async fn init_api_grub_mechanism(config: ApiConfigV2, rx: &mut Receiver<ApiConfigV2>) -> Result<()> { pub async fn init_api_grub_mechanism(config: ApiConfig, rx: &mut Receiver<ApiConfig>) -> Result<()> {
info!("Initializing API-info grubbing mechanism..."); info!("Initializing API-info grubbing mechanism...");
info!("Loading vars from .env file if exists...");
let _ = dotenv().ok();
let mut config = config; let mut config = config;
let mut poller = ApiPoll::new(&mut config).await; let mut poller = ApiPoll::new(&mut config).await;
let client = Exporter::init();
let shared_pool = Arc::new(client);
loop { loop {
let shared_pool = shared_pool.clone();
if poller.is_default().await { if poller.is_default().await {
sleep(Duration::from_secs(5)).await; sleep(Duration::from_secs(5)).await;
} else { } else {
@ -191,7 +87,7 @@ pub async fn init_api_grub_mechanism(config: ApiConfigV2, rx: &mut Receiver<ApiC
info!("Config changed"); info!("Config changed");
} }
} }
info!("Data from API: {:?}", poller.process_polling(shared_pool).await); info!("Data from API: {:?}", poller.process_polling().await);
sleep(Duration::from_secs(poller.get_delay().await as u64)).await; sleep(Duration::from_secs(poller.get_delay().await as u64)).await;
} }
} }
@ -202,6 +98,7 @@ pub async fn init_api_grub_mechanism(config: ApiConfigV2, rx: &mut Receiver<ApiC
mod net_unittests { mod net_unittests {
use super::*; use super::*;
use tokio::test; use tokio::test;
use integr_structs::api::{ApiConfig, ApiEndpoint};
#[test] #[test]
async fn check_str_to_rest_method() { async fn check_str_to_rest_method() {
@ -214,16 +111,16 @@ mod net_unittests {
} }
#[test] #[test]
async fn check_api_poll_change_config() { async fn check_api_poll_change_config() {
let mut conf1 = ApiConfigV2::default(); let mut conf1 = ApiConfig::default();
let conf2 = ApiConfigV2::pattern(); let conf2 = ApiConfig { endpoints : vec![], delay : 10, };
let mut poll = ApiPoll::new(&mut conf1).await; let mut poll = ApiPoll::new(&mut conf1).await;
poll.change_config(conf2).await; poll.change_config(conf2).await;
assert_eq!(poll.config.timeout, 1) assert_eq!(poll.config.delay, 10)
} }
#[test] #[test]
async fn check_api_poll_is_default() { async fn check_api_poll_is_default() {
let mut conf1 = ApiConfigV2::default(); let mut conf1 = ApiConfig::default();
let poll = ApiPoll::new(&mut conf1).await; let poll = ApiPoll::new(&mut conf1).await;
assert!(poll.is_default().await) assert!(poll.is_default().await)
} }
@ -233,14 +130,20 @@ mod net_unittests {
use log::{set_max_level, LevelFilter}; use log::{set_max_level, LevelFilter};
set_max_level(LevelFilter::Off); set_max_level(LevelFilter::Off);
let mut conf1 = ApiConfigV2::pattern(); let mut conf1 = ApiConfig {
let conf2 = ApiConfigV2::default(); endpoints : vec![
let exporter = Arc::new(Exporter::init()); ApiEndpoint {
url : String::from("https://dummy-json.mock.beeceptor.com/countries"),
method: String::from("get"),
}],
delay : 10,
};
let conf2 = ApiConfig::default();
let mut poll = ApiPoll::new(&mut conf1).await; let mut poll = ApiPoll::new(&mut conf1).await;
assert!(poll.process_polling(exporter.clone()).await.is_ok()); assert!(poll.process_polling().await.is_ok());
poll.change_config(conf2).await; poll.change_config(conf2).await;
assert!(poll.process_polling(exporter.clone()).await.is_err()); assert!(poll.process_polling().await.is_err());
} }
} }

View File

@ -2,7 +2,7 @@
// using Unix-Socket Client // using Unix-Socket Client
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use integr_structs::api::ApiConfigV2; use integr_structs::api::ApiConfig;
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
use tokio::net::UnixStream; use tokio::net::UnixStream;
use std::env; use std::env;
@ -18,7 +18,7 @@ enum UnixSocketConsumer {
// to create us-client // to create us-client
struct UnixSocketClient; struct UnixSocketClient;
// to catch new configs from integr submod // to catch new configs from integr submod
type ConfigGateway = Receiver<ApiConfigV2>; type ConfigGateway = Receiver<ApiConfig>;
pub async fn init_delivery_mech(rx: ConfigGateway) -> Result<()> { pub async fn init_delivery_mech(rx: ConfigGateway) -> Result<()> {
let mut rx = rx; let mut rx = rx;

View File

@ -1,18 +1,18 @@
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use integr_structs::api::ApiConfigV2; use integr_structs::api::ApiConfig;
use anyhow::Result; use anyhow::Result;
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
// mock // mock
type Worker = Arc<Sender<ApiConfigV2>>; type Worker = Arc<Sender<ApiConfig>>;
#[allow(dead_code, unused_variables)] #[allow(dead_code, unused_variables)]
pub async fn init_integration_mech(tx: Worker) -> Result<()> { pub async fn init_integration_mech(tx: Worker) -> Result<()> {
loop { loop {
sleep(Duration::from_secs(5)).await; sleep(Duration::from_secs(5)).await;
let conf = ApiConfigV2::default(); let conf = ApiConfig::default();
tx.send(conf).await?; tx.send(conf).await?;
} }
// Ok(()) // Ok(())

View File

@ -2,7 +2,7 @@ mod delivery;
mod integration; mod integration;
mod logger; mod logger;
use integr_structs::api::ApiConfigV2; use integr_structs::api::ApiConfig;
use logger::setup_logger; use logger::setup_logger;
use dotenv::dotenv; use dotenv::dotenv;
use anyhow::Result; use anyhow::Result;
@ -23,7 +23,7 @@ async fn main() -> Result<()> {
info!("Pulling env vars from .env file if exists ..."); info!("Pulling env vars from .env file if exists ...");
dotenv().ok(); dotenv().ok();
let (tx, rx) = mpsc::channel::<ApiConfigV2>(1); let (tx, rx) = mpsc::channel::<ApiConfig>(1);
let tx = Arc::new(tx); let tx = Arc::new(tx);
let integr_jh = tokio::spawn( let integr_jh = tokio::spawn(
async move { async move {

View File

@ -4,6 +4,5 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.95"
serde = { version = "1.0.217", features = ["derive"] } serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.135" serde_json = "1.0.135"

View File

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use serde_json::{ to_string_pretty, Value }; use serde_json::Value;
use anyhow::Result;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -29,87 +29,24 @@ impl Default for ApiConfig {
// v2 // v2
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct ApiConfigV2 { pub struct ApiConfigV2 {
pub id : u64, id : u64,
#[serde(default)] #[serde(default)]
pub template : Vec<Template>, template : Vec<Template>,
pub ip_address : String, ip_address : String,
pub login : Option<String>, login : Option<String>,
pub pass : Option<String>, pass : Option<String>,
pub api_key : Option<String>, api_key : Option<String>,
pub period : u32, // if "0" -> inf period : u32, // if "0" -> inf
pub timeout : u32, // if "0" -> no-delay timeout : u32, // if "0" -> no-delay
} }
impl Default for ApiConfigV2 { #[derive(Serialize, Deserialize, Debug)]
fn default() -> Self {
ApiConfigV2 {
id : 0,
template : Vec::new(),
ip_address : String::from("no_ip"),
login : None,
pass : None,
api_key : None,
period : 0,
timeout : 0,
}
}
}
impl ApiConfigV2 {
pub fn template() -> Self {
ApiConfigV2 {
id : 1111,
template : Vec::new(),
ip_address : String::from("ip"),
login : None,
pass : None,
api_key : None,
period : 1111,
timeout : 1111,
}
}
pub fn pattern() -> Self {
ApiConfigV2 {
id : 1111,
template : vec![
Template {
id : String::from("no id"),
name : String::from("open api"),
url : String::from("https://dummy-json.mock.beeceptor.com/countries"),
method : String::from("GET"),
measure : Vec::new(),
}
],
ip_address : String::from("ip"),
login : None,
pass : None,
api_key : None,
period : 1,
timeout : 1,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Template { pub struct Template {
pub id : String, id : String,
pub name : String, name : String,
pub url : String, url : String,
pub method : String,
#[serde(default)] #[serde(default)]
pub measure : Vec<String>, measure : Vec<String>,
}
impl Default for Template {
fn default() -> Self {
Template {
id : String::from("no-id"),
name : String::from("no-name"),
url : String::from("no-url"),
method : String::from("no-method"),
measure : Vec::new(),
}
}
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -117,31 +54,6 @@ pub struct ProcessedEndpoint {
id : String, id : String,
name : String, name : String,
url : String, url : String,
method : String,
#[serde(default)] #[serde(default)]
metrics : HashMap<String, Value>, metrics : HashMap<String, Value>
}
impl ProcessedEndpoint {
pub fn new(id: &str, name: &str, url: &str, method: &str, metrics: HashMap<String, Value>) -> Self {
ProcessedEndpoint {
id : id.to_owned(),
name : name.to_owned(),
url : url.to_owned(),
method : method.to_owned(),
metrics : metrics,
}
}
pub fn from_target_response(response: &str, keys: &Template) -> Result<String> {
let mut hm: HashMap<String, Value> = HashMap::new();
let mut response: Value = serde_json::from_str(response)?;
let _ = keys.measure.iter()
.map(|key| (key, response[key].take()))
.for_each(|(key, value)| {
hm.insert(key.clone(), value);
});
let val = ProcessedEndpoint::new(&keys.id, &keys.name, &keys.url, &keys.method,hm);
Ok(to_string_pretty(&val)?)
}
} }

View File

@ -2,30 +2,20 @@
"id" : 1 , "id" : 1 ,
"template" : "template" :
[{ [{
"id" :"mock_api_1", "id" :"conference",
"name" : "Mock / ", "name" : "Conference",
"url" : "http://127.0.0.1:8081/", "url" : "https://$ip_address/api/v1/conferences",
"method" : "GET",
"measure" : "measure" :
[ [
"operation", "response", "empty_field" "total", "number", "description",
"shutdown", "startTime", "stopTime",
"participants_total", "participants_online"
] ]
}, }],
{
"id" :"mock_api_2",
"name" : "Mock /ping ",
"url" : "http://127.0.0.1:8081/ping",
"method" : "GET",
"measure" :
[
"operation", "response", "empty_field"
]
}
],
"ip_address" : "127.0.0.1:8081", "ip_address" : "127.0.0.1:8081",
"login" : "", "login" : "",
"pass" : "" , "pass" : "" ,
"api_key" : "908c709827bd40n98r7209837x98273", "api_key" : "908c709827bd40n98r7209837x98273",
"period" : 10, "period" : "10",
"timeout" : 2 "timeout" : "2"
} }