feat,fix: add `get` cli command, cli commands don't panic

feat/type-attributes
Tomáš Mládek 2023-06-06 19:01:38 +02:00
parent d3c5d182af
commit 04d54f6e43
2 changed files with 86 additions and 16 deletions

View File

@ -23,9 +23,13 @@ lazy_static! {
upend::common::build::PROJECT_NAME, upend::common::build::PROJECT_NAME,
upend::common::build::PKG_VERSION upend::common::build::PKG_VERSION
); );
pub static ref REQWEST_CLIENT: reqwest::blocking::Client = reqwest::blocking::Client::builder() pub static ref REQWEST_CLIENT: reqwest::blocking::Client = reqwest::blocking::Client::builder()
.user_agent(APP_USER_AGENT.as_str()) .user_agent(APP_USER_AGENT.as_str())
.build() .build()
.unwrap(); .unwrap();
pub static ref REQWEST_ASYNC_CLIENT: reqwest::Client = reqwest::Client::builder()
.user_agent(APP_USER_AGENT.as_str())
.build()
.unwrap();
} }

View File

@ -1,6 +1,6 @@
#[macro_use] #[macro_use]
extern crate upend; extern crate upend;
use crate::common::{get_static_dir, REQWEST_CLIENT}; use crate::common::{get_static_dir, REQWEST_ASYNC_CLIENT};
use actix_cors::Cors; use actix_cors::Cors;
use actix_web::web::Data; use actix_web::web::Data;
use actix_web::{middleware, App, HttpServer}; use actix_web::{middleware, App, HttpServer};
@ -63,12 +63,24 @@ enum Commands {
#[arg(short, long, default_value = "tsv")] #[arg(short, long, default_value = "tsv")]
format: OutputFormat, format: OutputFormat,
}, },
Get {
/// URL of the UpEnd instance to query.
#[arg(short, long, default_value = "http://localhost:8093")]
url: Url,
/// The address of the entity; prefix a filepath by `=` to insert its hash.
entity: String,
/// The attribute to get the value(s) of. Optional.
attribute: Option<String>,
/// Output format
#[arg(short, long, default_value = "tsv")]
format: OutputFormat,
},
/// Insert an entry into an UpEnd server instance. /// Insert an entry into an UpEnd server instance.
Insert { Insert {
/// URL of the UpEnd instance to query. /// URL of the UpEnd instance to query.
#[arg(short, long)] #[arg(short, long, default_value = "http://localhost:8093")]
url: Option<Url>, url: Url,
/// The address of the entity; prefix a filepath by `@` to insert its hash. /// The address of the entity; prefix a filepath by `=` to insert its hash.
entity: String, entity: String,
/// The attribute of the entry. /// The attribute of the entry.
attribute: String, attribute: String,
@ -196,21 +208,73 @@ async fn main() -> Result<()> {
}) })
.to_string(); .to_string();
debug!("Final query: {}", query);
let api_url = url.join("/api/query")?; let api_url = url.join("/api/query")?;
debug!("Querying \"{}\"", api_url); debug!("Querying \"{}\": {}", api_url, query);
let response = REQWEST_CLIENT.post(api_url).body(query).send()?; let response = REQWEST_ASYNC_CLIENT.post(api_url).body(query).send().await?;
response.error_for_status_ref()?; response.error_for_status_ref()?;
match format { match format {
OutputFormat::Json | OutputFormat::Raw => println!("{}", response.text()?), OutputFormat::Json | OutputFormat::Raw => println!("{}", response.text().await?),
OutputFormat::Tsv => { OutputFormat::Tsv => {
eprintln!("entity\tattribute\tvalue\ttimestamp\tprovenance"); eprintln!("entity\tattribute\tvalue\ttimestamp\tprovenance");
response response
.json::<HashMap<String, serde_json::Value>>()? .json::<HashMap<String, serde_json::Value>>().await?
.iter()
.for_each(|(_, entry)| {
println!(
"{}\t{}\t{}\t{}\t{}",
entry.get("entity").and_then(|e| e.as_str()).unwrap(),
entry.get("attribute").and_then(|a| a.as_str()).unwrap(),
entry.get("value").and_then(|v| v.get("c")).unwrap(),
entry.get("timestamp").and_then(|t| t.as_str()).unwrap(),
entry.get("provenance").and_then(|p| p.as_str()).unwrap(),
)
})
}
}
Ok(())
}
Commands::Get {
url,
entity,
attribute,
format,
} => {
let api_url = url.join("/api/obj")?;
let entity = match entity {
entity if entity.starts_with('=') => hash_path(&entity[1..])?.to_string(),
entity if entity.starts_with("http") => Address::Url(entity.parse()?).to_string(),
_ => entity,
};
let query = format!(
"(matches @{} {} ?)",
entity,
attribute
.map(|a| format!("\"{a}\""))
.unwrap_or("?".to_string())
);
debug!("Querying \"{}\": {}", api_url, query);
let response = REQWEST_ASYNC_CLIENT
.get(api_url)
.body(query)
.send()
.await?;
response.error_for_status_ref()?;
match format {
OutputFormat::Json | OutputFormat::Raw => println!("{}", response.text().await?),
OutputFormat::Tsv => {
eprintln!("entity\tattribute\tvalue\ttimestamp\tprovenance");
response
.json::<HashMap<String, serde_json::Value>>()
.await?
.iter() .iter()
.for_each(|(_, entry)| { .for_each(|(_, entry)| {
println!( println!(
@ -234,7 +298,6 @@ async fn main() -> Result<()> {
value, value,
format: _, format: _,
} => { } => {
let url = url.unwrap_or("http://localhost:8093".parse().unwrap());
let api_url = url.join("/api/obj")?; let api_url = url.join("/api/obj")?;
let entity = match entity { let entity = match entity {
@ -252,15 +315,15 @@ async fn main() -> Result<()> {
}); });
debug!("Inserting {:?} at \"{}\"", body, api_url); debug!("Inserting {:?} at \"{}\"", body, api_url);
let response = REQWEST_CLIENT.put(api_url).json(&body).send()?; let response = REQWEST_ASYNC_CLIENT.put(api_url).json(&body).send().await?;
match response.error_for_status_ref() { match response.error_for_status_ref() {
Ok(_) => { Ok(_) => {
let data: Vec<String> = response.json()?; let data: Vec<String> = response.json().await?;
Ok(println!("{}", data[0])) Ok(println!("{}", data[0]))
} }
Err(err) => { Err(err) => {
error!("{}", response.text()?); error!("{}", response.text().await?);
Err(err.into()) Err(err.into())
} }
} }
@ -402,7 +465,10 @@ async fn main() -> Result<()> {
let app = App::new() let app = App::new()
.wrap(cors) .wrap(cors)
.wrap(middleware::DefaultHeaders::new().add(("UPEND-VERSION", upend::common::build::PKG_VERSION))) .wrap(
middleware::DefaultHeaders::new()
.add(("UPEND-VERSION", upend::common::build::PKG_VERSION)),
)
.app_data(actix_web::web::PayloadConfig::new(4_294_967_296)) .app_data(actix_web::web::PayloadConfig::new(4_294_967_296))
.app_data(Data::new(state.clone())) .app_data(Data::new(state.clone()))
.wrap(middleware::Logger::default().exclude("/api/jobs")) .wrap(middleware::Logger::default().exclude("/api/jobs"))