feat: filter while collecting
This commit is contained in:
parent
39c446485f
commit
b2af0b8c4f
3 changed files with 98 additions and 67 deletions
|
@ -1,50 +1,57 @@
|
|||
use postman_collection::{v1_0_0, v2_0_0, v2_1_0};
|
||||
use regex::Regex;
|
||||
|
||||
use super::request::IntoRequest;
|
||||
use super::{request::IntoRequest, description::IntoOptionalString};
|
||||
|
||||
pub enum RequestNode {
|
||||
Leaf(reqwest::RequestBuilder),
|
||||
Leaf(reqwest::Request),
|
||||
Branch(Vec<RequestTree>),
|
||||
}
|
||||
|
||||
pub struct RequestTree {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub request: RequestNode,
|
||||
}
|
||||
|
||||
pub trait CollectRequests {
|
||||
fn collect_requests(&self) -> RequestTree;
|
||||
fn collect_requests(&self, filter: Option<&Regex>) -> Option<RequestTree>;
|
||||
}
|
||||
|
||||
impl CollectRequests for v1_0_0::Spec {
|
||||
fn collect_requests(&self) -> RequestTree {
|
||||
fn collect_requests(&self, _filter: Option<&Regex>) -> Option<RequestTree> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectRequests for v2_0_0::Spec {
|
||||
fn collect_requests(&self) -> RequestTree {
|
||||
let mut requests = Vec::new();
|
||||
for item in &self.item {
|
||||
requests.push(item.collect_requests());
|
||||
}
|
||||
RequestTree {
|
||||
name: self.info.name.clone(),
|
||||
request: RequestNode::Branch(requests),
|
||||
}
|
||||
fn collect_requests(&self, filter: Option<&Regex>) -> Option<RequestTree> {
|
||||
let requests = self.item.iter()
|
||||
.filter_map(|x| x.collect_requests(filter))
|
||||
.collect::<Vec<RequestTree>>();
|
||||
(!requests.is_empty())
|
||||
.then(|| RequestTree {
|
||||
name: self.info.name.clone(),
|
||||
description: self.info.description.as_ref().map_or(None, |x| x.as_string()),
|
||||
request: RequestNode::Branch(requests),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectRequests for v2_1_0::Spec {
|
||||
fn collect_requests(&self) -> RequestTree {
|
||||
let mut requests = Vec::new();
|
||||
for item in &self.item {
|
||||
requests.push(item.collect_requests());
|
||||
}
|
||||
RequestTree {
|
||||
name: self.info.name.clone(),
|
||||
request: RequestNode::Branch(requests),
|
||||
}
|
||||
fn collect_requests(&self, filter: Option<&Regex>) -> Option<RequestTree> {
|
||||
let requests = self.item.iter()
|
||||
.filter_map(|x| x.collect_requests(filter))
|
||||
.collect::<Vec<RequestTree>>();
|
||||
(!requests.is_empty())
|
||||
.then(||
|
||||
RequestTree {
|
||||
name: self.info.name.clone(),
|
||||
description: self.info.description.as_ref().map_or(None, |x| x.as_string()),
|
||||
request: RequestNode::Branch(requests),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,11 +62,12 @@ impl CollectRequests for v2_1_0::Spec {
|
|||
// }
|
||||
|
||||
impl CollectRequests for v2_0_0::Items {
|
||||
fn collect_requests(&self) -> RequestTree {
|
||||
let request : RequestNode;
|
||||
fn collect_requests(&self, filter: Option<&Regex>) -> Option<RequestTree> {
|
||||
if self.request.is_some() && self.item.is_some() {
|
||||
panic!("some node has both a single request and child requests!");
|
||||
panic!("node has both a single request and child requests!");
|
||||
}
|
||||
let name = self.name.as_ref().unwrap_or(&"".to_string()).to_string();
|
||||
let description = self.description.as_ref().map_or(None, |x| x.as_string());
|
||||
if let Some(r) = &self.request {
|
||||
let clazz = match r {
|
||||
v2_0_0::RequestUnion::String(url) => v2_0_0::RequestClass {
|
||||
|
@ -68,26 +76,26 @@ impl CollectRequests for v2_0_0::Items {
|
|||
},
|
||||
v2_0_0::RequestUnion::RequestClass(r) => r.clone(),
|
||||
};
|
||||
request = RequestNode::Leaf(clazz.make_request());
|
||||
Some(RequestTree { name, description, request: RequestNode::Leaf(clazz.make_request(filter)?) })
|
||||
} else if let Some(sub) = &self.item {
|
||||
let mut requests = Vec::new();
|
||||
for item in sub {
|
||||
requests.push(item.collect_requests());
|
||||
}
|
||||
request = RequestNode::Branch(requests);
|
||||
let requests = sub.iter()
|
||||
.filter_map(|x| x.collect_requests(filter))
|
||||
.collect::<Vec<RequestTree>>();
|
||||
(!requests.is_empty())
|
||||
.then(|| RequestTree { name, description, request: RequestNode::Branch(requests) })
|
||||
} else {
|
||||
request = RequestNode::Branch(Vec::new()); // TODO make if/elseif/else nicer?
|
||||
}
|
||||
RequestTree {
|
||||
name: self.name.as_ref().unwrap_or(&"".to_string()).to_string(), // TODO meme
|
||||
request,
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectRequests for v2_1_0::Items {
|
||||
fn collect_requests(&self) -> RequestTree {
|
||||
let request : RequestNode;
|
||||
fn collect_requests(&self, filter: Option<&Regex>) -> Option<RequestTree> {
|
||||
if self.request.is_some() && self.item.is_some() {
|
||||
panic!("node has both a single request and child requests!");
|
||||
}
|
||||
let name = self.name.as_ref().unwrap_or(&"".to_string()).to_string();
|
||||
let description = self.description.as_ref().map_or(None, |x| x.as_string());
|
||||
if let Some(r) = &self.request {
|
||||
let clazz = match r {
|
||||
v2_1_0::RequestUnion::String(url) => v2_1_0::RequestClass {
|
||||
|
@ -102,19 +110,15 @@ impl CollectRequests for v2_1_0::Items {
|
|||
},
|
||||
v2_1_0::RequestUnion::RequestClass(r) => r.clone(),
|
||||
};
|
||||
request = RequestNode::Leaf(clazz.make_request());
|
||||
Some(RequestTree { name, description, request: RequestNode::Leaf(clazz.make_request(filter)?) })
|
||||
} else if let Some(sub) = &self.item {
|
||||
let mut requests = Vec::new();
|
||||
for item in sub {
|
||||
requests.push(item.collect_requests());
|
||||
}
|
||||
request = RequestNode::Branch(requests);
|
||||
let requests = sub.iter()
|
||||
.filter_map(|x| x.collect_requests(filter))
|
||||
.collect::<Vec<RequestTree>>();
|
||||
(!requests.is_empty())
|
||||
.then(|| RequestTree { name, description, request: RequestNode::Branch(requests) })
|
||||
} else {
|
||||
request = RequestNode::Branch(Vec::new()); // TODO make if/elseif/else nicer?
|
||||
}
|
||||
RequestTree {
|
||||
name: self.name.as_ref().unwrap_or(&"".to_string()).to_string(), // TODO meme
|
||||
request,
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
pub mod request;
|
||||
pub mod collector;
|
||||
pub mod description;
|
||||
|
||||
use postman_collection::{PostmanCollection, v2_0_0, v2_1_0};
|
||||
use regex::Regex;
|
||||
|
||||
use self::collector::{CollectRequests, RequestTree};
|
||||
|
||||
|
@ -52,11 +54,11 @@ impl PostWomanCollection {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn requests(&self) -> RequestTree {
|
||||
pub fn requests(&self, filter: Option<&Regex>) -> Option<RequestTree> {
|
||||
match &self.collection {
|
||||
PostmanCollection::V1_0_0(_) => todo!(),
|
||||
PostmanCollection::V2_0_0(spec) => spec.collect_requests(),
|
||||
PostmanCollection::V2_1_0(spec) => spec.collect_requests(),
|
||||
PostmanCollection::V2_0_0(spec) => spec.collect_requests(filter),
|
||||
PostmanCollection::V2_1_0(spec) => spec.collect_requests(filter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use postman_collection::{v1_0_0, v2_0_0, v2_1_0};
|
||||
use regex::Regex;
|
||||
|
||||
fn fill_from_env(mut txt: String) -> String {
|
||||
for (k, v) in std::env::vars() {
|
||||
let key = format!("{{{{{}}}}}", k);
|
||||
let key = format!("{{{{{}}}}}", k); // escaping of { is done by repeating them
|
||||
if txt.contains(&key) {
|
||||
txt = txt.replace(&key, &v);
|
||||
}
|
||||
|
@ -13,11 +14,11 @@ fn fill_from_env(mut txt: String) -> String {
|
|||
}
|
||||
|
||||
pub trait IntoRequest {
|
||||
fn make_request(&self) -> reqwest::RequestBuilder;
|
||||
fn make_request(&self, filter: Option<&Regex>) -> Option<reqwest::Request>;
|
||||
}
|
||||
|
||||
impl IntoRequest for v2_0_0::RequestClass {
|
||||
fn make_request(&self) -> reqwest::RequestBuilder {
|
||||
fn make_request(&self, filter: Option<&Regex>) -> Option<reqwest::Request> {
|
||||
let method = reqwest::Method::from_bytes(
|
||||
self.method.as_ref().unwrap_or(&"GET".into()).as_bytes() // TODO lol?
|
||||
).unwrap_or(reqwest::Method::GET); // TODO throw an error rather than replacing it silently
|
||||
|
@ -31,19 +32,27 @@ impl IntoRequest for v2_0_0::RequestClass {
|
|||
|
||||
url_str = fill_from_env(url_str);
|
||||
|
||||
if filter.is_some() && !filter.unwrap().is_match(&url_str) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let url = reqwest::Url::from_str(&url_str).unwrap_or_else(|e| {
|
||||
eprintln!("error creating url ({}), falling back to localhost", e);
|
||||
reqwest::Url::from_str("http://localhost/").unwrap()
|
||||
});
|
||||
|
||||
let mut out = reqwest::Client::new().request(method, url);
|
||||
let mut request = reqwest::Request::new(method, url);
|
||||
|
||||
// TODO handle more auth types than just bearer
|
||||
if let Some(auth) = &self.auth {
|
||||
if let Some(bearers) = &auth.bearer {
|
||||
let headers = request.headers_mut();
|
||||
for v in bearers.values() {
|
||||
if let Some(value) = &v {
|
||||
out = out.header("Authorization", format!("Bearer {}", value.as_str().unwrap_or(&value.to_string())))
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
reqwest::header::HeaderValue::from_str(&format!("Bearer {}", value.as_str().unwrap_or(&value.to_string()))).unwrap() // TODO lmao meme
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,10 +60,14 @@ impl IntoRequest for v2_0_0::RequestClass {
|
|||
|
||||
match &self.header {
|
||||
Some(v2_0_0::HeaderUnion::HeaderArray(x)) => {
|
||||
let headers = request.headers_mut();
|
||||
for h in x {
|
||||
let k = fill_from_env(h.key.clone());
|
||||
let v = fill_from_env(h.value.clone());
|
||||
out = out.header(k, v); // TODO avoid cloning
|
||||
headers.insert(
|
||||
reqwest::header::HeaderName::from_str(&k).unwrap(),
|
||||
reqwest::header::HeaderValue::from_str(&v).unwrap()
|
||||
); // TODO avoid cloning
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
@ -63,17 +76,17 @@ impl IntoRequest for v2_0_0::RequestClass {
|
|||
|
||||
match &self.body {
|
||||
Some(v2_0_0::Body { raw: Some(x), .. }) => {
|
||||
out = out.body(fill_from_env(x.clone())) // TODO try to avoid cloning?
|
||||
*request.body_mut() = Some(reqwest::Body::from(fill_from_env(x.clone()))) // TODO try to avoid cloning?
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
out
|
||||
Some(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRequest for v2_1_0::RequestClass {
|
||||
fn make_request(&self) -> reqwest::RequestBuilder {
|
||||
fn make_request(&self, filter: Option<&Regex>) -> Option<reqwest::Request> {
|
||||
let method = reqwest::Method::from_bytes(
|
||||
self.method.as_ref().unwrap_or(&"GET".into()).as_bytes() // TODO lol?
|
||||
).unwrap_or(reqwest::Method::GET); // TODO throw an error rather than replacing it silently
|
||||
|
@ -87,19 +100,27 @@ impl IntoRequest for v2_1_0::RequestClass {
|
|||
|
||||
url_str = fill_from_env(url_str);
|
||||
|
||||
if filter.is_some() && !filter.unwrap().is_match(&url_str) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let url = reqwest::Url::from_str(&url_str).unwrap_or_else(|e| {
|
||||
eprintln!("error creating url ({}), falling back to localhost", e);
|
||||
reqwest::Url::from_str("http://localhost/").unwrap()
|
||||
});
|
||||
|
||||
let mut out = reqwest::Client::new().request(method, url);
|
||||
let mut request = reqwest::Request::new(method, url);
|
||||
|
||||
// TODO handle more auth types than just bearer
|
||||
if let Some(auth) = &self.auth {
|
||||
if let Some(bearers) = &auth.bearer {
|
||||
let headers = request.headers_mut();
|
||||
for bearer in bearers {
|
||||
if let Some(value) = &bearer.value {
|
||||
out = out.header("Authorization", format!("Bearer {}", value.as_str().unwrap_or(&value.to_string())))
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
reqwest::header::HeaderValue::from_str(&format!("Bearer {}", value.as_str().unwrap_or(&value.to_string()))).unwrap() // TODO lmao meme
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,10 +128,14 @@ impl IntoRequest for v2_1_0::RequestClass {
|
|||
|
||||
match &self.header {
|
||||
Some(v2_1_0::HeaderUnion::HeaderArray(x)) => {
|
||||
let headers = request.headers_mut();
|
||||
for h in x {
|
||||
let k = fill_from_env(h.key.clone());
|
||||
let v = fill_from_env(h.value.clone());
|
||||
out = out.header(k, v); // TODO avoid cloning
|
||||
headers.insert(
|
||||
reqwest::header::HeaderName::from_str(&k).unwrap(),
|
||||
reqwest::header::HeaderValue::from_str(&v).unwrap()
|
||||
); // TODO avoid cloning
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
@ -119,17 +144,17 @@ impl IntoRequest for v2_1_0::RequestClass {
|
|||
|
||||
match &self.body {
|
||||
Some(v2_1_0::Body { raw: Some(x), .. }) => {
|
||||
out = out.body(fill_from_env(x.clone())) // TODO try to avoid cloning?
|
||||
*request.body_mut() = Some(reqwest::Body::from(fill_from_env(x.clone()))) // TODO try to avoid cloning?
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
out
|
||||
Some(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRequest for v1_0_0::Request {
|
||||
fn make_request(&self) -> reqwest::RequestBuilder {
|
||||
fn make_request(&self, _filter: Option<&Regex>) -> Option<reqwest::Request> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue