Compare commits
6 commits
fe7bc43c57
...
3a966bdc8b
Author | SHA1 | Date | |
---|---|---|---|
3a966bdc8b | |||
763fa7fbcb | |||
f6577e6f63 | |||
72c5b443db | |||
543f258724 | |||
9013515290 |
6 changed files with 54 additions and 141 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -507,6 +507,7 @@ checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -753,6 +754,7 @@ version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"http",
|
"http",
|
||||||
|
"indexmap",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1250,6 +1252,7 @@ version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
|
|
|
@ -8,11 +8,12 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
http = "1.1.0"
|
http = "1.1.0"
|
||||||
|
indexmap = { version = "2.6", features = ["serde"] }
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
thiserror = "1.0.64"
|
thiserror = "1.0.64"
|
||||||
tokio = { version = "1.40", features = ["full"] }
|
tokio = { version = "1.40", features = ["full"] }
|
||||||
toml = "0.8"
|
toml = { version = "0.8", features = ["preserve_order"] }
|
||||||
toml_edit = { version = "0.22", features = ["serde"] } # only to pretty print tables ...
|
toml_edit = { version = "0.22", features = ["serde"] } # only to pretty print tables ...
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
{
|
|
||||||
"variables": [],
|
|
||||||
"info": {
|
|
||||||
"name": "Sample Postman Collection",
|
|
||||||
"_postman_id": "35567af6-6b92-26c2-561a-21fe8aeeb1ea",
|
|
||||||
"description": "A sample collection to demonstrate collections as a set of related requests",
|
|
||||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
|
||||||
},
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "GET request",
|
|
||||||
"request": {
|
|
||||||
"url": {
|
|
||||||
"raw": "https://api.alemi.dev/dump?source=sample-collection",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"alemi",
|
|
||||||
"dev"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"dump"
|
|
||||||
],
|
|
||||||
"query": [
|
|
||||||
{
|
|
||||||
"key": "source",
|
|
||||||
"value": "sample-collection",
|
|
||||||
"equals": true,
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variable": []
|
|
||||||
},
|
|
||||||
"method": "GET",
|
|
||||||
"header": [],
|
|
||||||
"body": {},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "POST requests",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "Text body",
|
|
||||||
"request": {
|
|
||||||
"url": "https://api.alemi.dev/dump",
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "text/plain",
|
|
||||||
"type": "default"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium..."
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Json body",
|
|
||||||
"request": {
|
|
||||||
"url": "https://api.alemi.dev/dump",
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\"text\":\"Lorem ipsum\", \"length\":100}"
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
33
postwoman.toml
Normal file
33
postwoman.toml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
[client]
|
||||||
|
user_agent = "postwoman@sample/0.2.0"
|
||||||
|
|
||||||
|
[route.healthcheck]
|
||||||
|
url = "https://api.alemi.dev/"
|
||||||
|
|
||||||
|
[route.debug]
|
||||||
|
url = "https://api.alemi.dev/debug"
|
||||||
|
method = "PUT"
|
||||||
|
headers = [
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Authorization: Bearer ${PW_TOKEN}",
|
||||||
|
]
|
||||||
|
body = { hello = "world!", success = true }
|
||||||
|
extract = { type = "body" }
|
||||||
|
|
||||||
|
[route.payload]
|
||||||
|
url = "https://api.alemi.dev/debug"
|
||||||
|
method = "POST"
|
||||||
|
body = '''{
|
||||||
|
"complex": {
|
||||||
|
"json": "payloads",
|
||||||
|
"can": "be",
|
||||||
|
"expressed": "this",
|
||||||
|
"way": true
|
||||||
|
}
|
||||||
|
}'''
|
||||||
|
extract = { type = "body" }
|
||||||
|
|
||||||
|
[route.cookie]
|
||||||
|
url = "https://api.alemi.dev/getcookie"
|
||||||
|
method = "GET"
|
||||||
|
extract = { type = "header", key = "Set-Cookie" }
|
50
src/main.rs
50
src/main.rs
|
@ -1,9 +1,7 @@
|
||||||
mod model;
|
mod model;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use model::{Endpoint, Extractor, PostWomanClient, PostWomanConfig, PostWomanError, StringOr};
|
use model::{PostWomanConfig, PostWomanError};
|
||||||
|
|
||||||
static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
|
static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
|
@ -22,9 +20,6 @@ struct PostWomanArgs {
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub enum PostWomanActions {
|
pub enum PostWomanActions {
|
||||||
/// print an example configragion, pipe to file and start editing
|
|
||||||
Sample,
|
|
||||||
|
|
||||||
/// execute specific endpoint requests
|
/// execute specific endpoint requests
|
||||||
Run {
|
Run {
|
||||||
/// regex query filter, run all with '.*'
|
/// regex query filter, run all with '.*'
|
||||||
|
@ -49,45 +44,6 @@ pub enum PostWomanActions {
|
||||||
async fn main() -> Result<(), PostWomanError> {
|
async fn main() -> Result<(), PostWomanError> {
|
||||||
let args = PostWomanArgs::parse();
|
let args = PostWomanArgs::parse();
|
||||||
|
|
||||||
if matches!(args.action, PostWomanActions::Sample) {
|
|
||||||
let a = Endpoint {
|
|
||||||
url: "https://api.alemi.dev/debug".into(),
|
|
||||||
query: None,
|
|
||||||
method: None,
|
|
||||||
headers: None,
|
|
||||||
body: None,
|
|
||||||
extract: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = Endpoint {
|
|
||||||
url: "https://api.alemi.dev/debug".into(),
|
|
||||||
query: None,
|
|
||||||
method: Some("PUT".into()),
|
|
||||||
headers: Some(vec![
|
|
||||||
"Authorization: Bearer asdfg".into(),
|
|
||||||
"Cache: skip".into(),
|
|
||||||
]),
|
|
||||||
body: Some(StringOr::T(toml::Table::from_iter([("hello".into(), toml::Value::String("world".into()))]))),
|
|
||||||
extract: Some(StringOr::T(Extractor::Body)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = PostWomanClient {
|
|
||||||
user_agent: Some(APP_USER_AGENT.into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cfg = PostWomanConfig {
|
|
||||||
client,
|
|
||||||
route: HashMap::from_iter([
|
|
||||||
("simple".to_string(), a),
|
|
||||||
("json".to_string(), b),
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("{}", toml_edit::ser::to_string_pretty(&cfg)?);
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let collection = std::fs::read_to_string(args.collection)?;
|
let collection = std::fs::read_to_string(args.collection)?;
|
||||||
let config: PostWomanConfig = toml::from_str(&collection)?;
|
let config: PostWomanConfig = toml::from_str(&collection)?;
|
||||||
|
|
||||||
|
@ -99,7 +55,7 @@ async fn main() -> Result<(), PostWomanError> {
|
||||||
eprintln!("> executing {name}");
|
eprintln!("> executing {name}");
|
||||||
let res = endpoint
|
let res = endpoint
|
||||||
.fill()
|
.fill()
|
||||||
.execute()
|
.execute(&config.client)
|
||||||
.await?;
|
.await?;
|
||||||
println!("{res}");
|
println!("{res}");
|
||||||
}
|
}
|
||||||
|
@ -109,8 +65,6 @@ async fn main() -> Result<(), PostWomanError> {
|
||||||
// PostWomanActions::Save { name, url, method, headers, body } => {
|
// PostWomanActions::Save { name, url, method, headers, body } => {
|
||||||
// todo!();
|
// todo!();
|
||||||
// },
|
// },
|
||||||
|
|
||||||
PostWomanActions::Sample => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
21
src/model.rs
21
src/model.rs
|
@ -1,6 +1,8 @@
|
||||||
use std::{collections::HashMap, str::FromStr};
|
use std::str::FromStr;
|
||||||
|
|
||||||
use reqwest::{header::{HeaderMap, HeaderName, HeaderValue}};
|
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
||||||
|
|
||||||
|
use crate::APP_USER_AGENT;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum PostWomanError {
|
pub enum PostWomanError {
|
||||||
|
@ -85,7 +87,7 @@ fn replace_recursive(element: toml::Value, from: &str, to: &str) -> toml::Value
|
||||||
impl Endpoint {
|
impl Endpoint {
|
||||||
pub fn fill(mut self) -> Self {
|
pub fn fill(mut self) -> Self {
|
||||||
for (k, v) in std::env::vars() {
|
for (k, v) in std::env::vars() {
|
||||||
let k_var = format!("${k}");
|
let k_var = format!("${{{k}}}");
|
||||||
self.url = self.url.replace(&k_var, &v);
|
self.url = self.url.replace(&k_var, &v);
|
||||||
if let Some(method) = self.method {
|
if let Some(method) = self.method {
|
||||||
self.method = Some(method.replace(&k_var, &v));
|
self.method = Some(method.replace(&k_var, &v));
|
||||||
|
@ -115,7 +117,7 @@ impl Endpoint {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute(self) -> Result<String, PostWomanError> {
|
pub async fn execute(self, opts: &PostWomanClient) -> Result<String, PostWomanError> {
|
||||||
let method = match self.method {
|
let method = match self.method {
|
||||||
Some(m) => reqwest::Method::from_str(&m)?,
|
Some(m) => reqwest::Method::from_str(&m)?,
|
||||||
None => reqwest::Method::GET,
|
None => reqwest::Method::GET,
|
||||||
|
@ -133,7 +135,12 @@ impl Endpoint {
|
||||||
StringOr::Str(x) => x,
|
StringOr::Str(x) => x,
|
||||||
StringOr::T(json) => serde_json::to_string(&json)?,
|
StringOr::T(json) => serde_json::to_string(&json)?,
|
||||||
};
|
};
|
||||||
let res = reqwest::Client::new()
|
|
||||||
|
let client = reqwest::Client::builder()
|
||||||
|
.user_agent(opts.user_agent.as_deref().unwrap_or(APP_USER_AGENT))
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
let res = client
|
||||||
.request(method, self.url)
|
.request(method, self.url)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.body(body)
|
.body(body)
|
||||||
|
@ -142,7 +149,7 @@ impl Endpoint {
|
||||||
.error_for_status()?;
|
.error_for_status()?;
|
||||||
|
|
||||||
Ok(match self.extract.unwrap_or_default() {
|
Ok(match self.extract.unwrap_or_default() {
|
||||||
StringOr::Str(query) => todo!(),
|
StringOr::Str(_query) => todo!(),
|
||||||
StringOr::T(Extractor::Debug) => format!("{res:#?}"),
|
StringOr::T(Extractor::Debug) => format!("{res:#?}"),
|
||||||
StringOr::T(Extractor::Body) => res.text().await?,
|
StringOr::T(Extractor::Body) => res.text().await?,
|
||||||
StringOr::T(Extractor::Header { key }) => res
|
StringOr::T(Extractor::Header { key }) => res
|
||||||
|
@ -188,5 +195,5 @@ pub struct PostWomanClient {
|
||||||
pub struct PostWomanConfig {
|
pub struct PostWomanConfig {
|
||||||
pub client: PostWomanClient,
|
pub client: PostWomanClient,
|
||||||
// it's weird to name it singular but makes more sense in config
|
// it's weird to name it singular but makes more sense in config
|
||||||
pub route: HashMap<String, Endpoint>,
|
pub route: indexmap::IndexMap<String, Endpoint>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue