Compare commits
5 commits
0713f323bb
...
ddadfe1253
Author | SHA1 | Date | |
---|---|---|---|
ddadfe1253 | |||
8cfa2688fd | |||
cab02a79c5 | |||
2e520129a2 | |||
b0b67fd1be |
7 changed files with 108 additions and 48 deletions
48
.github/workflows/test.yml
vendored
Normal file
48
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
types: [synchronize, review_requested]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
- beta
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.toolchain }}
|
||||||
|
- run: cargo test --verbose
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
needs: [test]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
runner:
|
||||||
|
- ubuntu-latest
|
||||||
|
- windows-latest
|
||||||
|
- macos-latest
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
- beta
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.toolchain }}
|
||||||
|
- run: cargo build --release --verbose
|
72
README.md
72
README.md
|
@ -96,41 +96,27 @@ $ postwoman
|
||||||
Run all endpoints matching `.` (aka all of them)
|
Run all endpoints matching `.` (aka all of them)
|
||||||
```
|
```
|
||||||
$ postwoman run .
|
$ postwoman run .
|
||||||
: [22:20:53.112717] sending healthcheck ...
|
: [22:27:17.960461] sending healthcheck ...
|
||||||
+ [22:20:53.250755] healthcheck done in 138ms
|
+ [22:27:18.109843] healthcheck done in 149ms
|
||||||
Response {
|
{
|
||||||
url: "https://api.alemi.dev/",
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
"server": "nginx/1.26.2",
|
|
||||||
"date": "Sat, 19 Oct 2024 20:20:53 GMT",
|
|
||||||
"content-type": "application/json",
|
|
||||||
"content-length": "161",
|
|
||||||
"connection": "keep-alive",
|
|
||||||
"vary": "Accept-Encoding",
|
|
||||||
"access-control-allow-origin": "*",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Body: {
|
|
||||||
"example": [
|
"example": [
|
||||||
"https://api.alemi.dev/debug",
|
"https://api.alemi.dev/debug",
|
||||||
"https://api.alemi.dev/msg",
|
"https://api.alemi.dev/msg",
|
||||||
"https://api.alemi.dev/mumble/ping"
|
"https://api.alemi.dev/mumble/ping"
|
||||||
],
|
],
|
||||||
"time": "Saturday, 19-Oct-2024 20:20:53 GMT",
|
"time": "Saturday, 19-Oct-2024 20:27:18 GMT",
|
||||||
"up": true
|
"up": true
|
||||||
}
|
}
|
||||||
|
: [22:27:18.109924] sending debug ...
|
||||||
: [22:20:53.250802] sending debug ...
|
+ [22:27:18.268383] debug done in 158ms
|
||||||
+ [22:20:53.369298] debug done in 118ms
|
|
||||||
/debug?body=json&cache=0
|
/debug?body=json&cache=0
|
||||||
: [22:20:53.369352] sending benchmark ...
|
: [22:27:18.268477] sending benchmark ...
|
||||||
+ [22:20:53.482720] benchmark done in 113ms
|
+ [22:27:18.422707] benchmark done in 154ms
|
||||||
: [22:20:53.482745] sending notfound ...
|
: [22:27:18.422775] sending notfound ...
|
||||||
+ [22:20:53.593134] notfound done in 110ms
|
+ [22:27:18.575942] notfound done in 153ms
|
||||||
nginx/1.26.2
|
nginx/1.26.2
|
||||||
: [22:20:53.593163] sending payload ...
|
: [22:27:18.576010] sending payload ...
|
||||||
+ [22:20:53.709245] payload done in 116ms
|
+ [22:27:18.732582] payload done in 156ms
|
||||||
{
|
{
|
||||||
"body": "{\n\t\"complex\": {\n\t\t\"json\": \"payloads\",\n\t\t\"can\": \"be\",\n\t\t\"expressed\": \"this\",\n\t\t\"way\": true\n\t}\n}",
|
"body": "{\n\t\"complex\": {\n\t\t\"json\": \"payloads\",\n\t\t\"can\": \"be\",\n\t\t\"expressed\": \"this\",\n\t\t\"way\": true\n\t}\n}",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
@ -142,15 +128,41 @@ nginx/1.26.2
|
||||||
"user-agent": "postwoman@sample/0.2.0",
|
"user-agent": "postwoman@sample/0.2.0",
|
||||||
"x-forwarded-proto": "https",
|
"x-forwarded-proto": "https",
|
||||||
"x-real-ip": "93.34.149.115",
|
"x-real-ip": "93.34.149.115",
|
||||||
"x-real-port": 46437,
|
"x-real-port": 46695,
|
||||||
"x-user-agent": "postwoman@sample/0.2.0"
|
"x-user-agent": "postwoman@sample/0.2.0"
|
||||||
},
|
},
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"path": "/debug",
|
"path": "/debug",
|
||||||
"time": 1729369253.6897264,
|
"time": 1729369638.7079802,
|
||||||
"version": "HTTP/1.0"
|
"version": "HTTP/1.0"
|
||||||
}
|
}
|
||||||
: [22:20:53.709276] sending cookie ...
|
: [22:27:18.732676] sending cookie ...
|
||||||
+ [22:20:53.822684] cookie done in 113ms
|
+ [22:27:18.886862] cookie done in 154ms
|
||||||
SGF2ZSBhIENvb2tpZSE=
|
SGF2ZSBhIENvb2tpZSE=
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Debug a specific route passing `--debug`:
|
||||||
|
```
|
||||||
|
$ postwoman run notfound --debug
|
||||||
|
: [22:26:59.045642] sending notfound ...
|
||||||
|
+ [22:26:59.220103] notfound done in 174ms
|
||||||
|
Response {
|
||||||
|
url: "https://api.alemi.dev/not-found",
|
||||||
|
status: 404,
|
||||||
|
headers: {
|
||||||
|
"server": "nginx/1.26.2",
|
||||||
|
"date": "Sat, 19 Oct 2024 20:26:59 GMT",
|
||||||
|
"content-type": "text/html",
|
||||||
|
"content-length": "153",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Body: <html>
|
||||||
|
<head><title>404 Not Found</title></head>
|
||||||
|
<body>
|
||||||
|
<center><h1>404 Not Found</h1></center>
|
||||||
|
<hr><center>nginx/1.26.2</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
12
src/ext.rs
12
src/ext.rs
|
@ -1,3 +1,15 @@
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum StringOr<T> {
|
||||||
|
Str(String),
|
||||||
|
T(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> Default for StringOr<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::T(T::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stringify_toml(v: &toml::Value) -> String {
|
pub fn stringify_toml(v: &toml::Value) -> String {
|
||||||
match v {
|
match v {
|
||||||
|
|
|
@ -100,7 +100,7 @@ async fn main() -> Result<(), PostWomanError> {
|
||||||
let env = Arc::new(config.env);
|
let env = Arc::new(config.env);
|
||||||
for (name, mut endpoint) in config.route {
|
for (name, mut endpoint) in config.route {
|
||||||
if pattern.find(&name).is_some() {
|
if pattern.find(&name).is_some() {
|
||||||
if debug { endpoint.extract = None };
|
if debug { endpoint.extract = Some(ext::StringOr::T(model::Extractor::Debug)) };
|
||||||
for i in 0..repeat {
|
for i in 0..repeat {
|
||||||
let suffix = if repeat > 1 {
|
let suffix = if repeat > 1 {
|
||||||
format!("#{} ", i+1)
|
format!("#{} ", i+1)
|
||||||
|
|
|
@ -6,7 +6,8 @@ use jaq_interpret::FilterT;
|
||||||
|
|
||||||
use crate::{PostWomanError, APP_USER_AGENT};
|
use crate::{PostWomanError, APP_USER_AGENT};
|
||||||
|
|
||||||
use super::{Extractor, PostWomanClient, StringOr};
|
use crate::ext::{stringify_toml, stringify_json, StringOr};
|
||||||
|
use super::{Extractor, PostWomanClient};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -59,7 +60,7 @@ impl Endpoint {
|
||||||
vars.insert("POSTWOMAN_TIMESTAMP".to_string(), chrono::Local::now().timestamp().to_string());
|
vars.insert("POSTWOMAN_TIMESTAMP".to_string(), chrono::Local::now().timestamp().to_string());
|
||||||
|
|
||||||
for (k, v) in env {
|
for (k, v) in env {
|
||||||
vars.insert(k.to_string(), crate::ext::stringify_toml(v));
|
vars.insert(k.to_string(), stringify_toml(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (k, v) in std::env::vars() {
|
for (k, v) in std::env::vars() {
|
||||||
|
@ -173,7 +174,7 @@ impl Endpoint {
|
||||||
let json: serde_json::Value = res.json().await?;
|
let json: serde_json::Value = res.json().await?;
|
||||||
let selection = jq(&query, json)?;
|
let selection = jq(&query, json)?;
|
||||||
if selection.len() == 1 {
|
if selection.len() == 1 {
|
||||||
crate::ext::stringify_json(&selection[0]) + "\n"
|
stringify_json(&selection[0]) + "\n"
|
||||||
} else {
|
} else {
|
||||||
serde_json::to_string_pretty(&selection)? + "\n"
|
serde_json::to_string_pretty(&selection)? + "\n"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
#[serde(tag = "type", rename_all = "lowercase")]
|
#[serde(tag = "type", rename_all = "lowercase")]
|
||||||
pub enum Extractor {
|
pub enum Extractor {
|
||||||
#[default]
|
#[default]
|
||||||
Debug,
|
|
||||||
Body,
|
Body,
|
||||||
|
Debug,
|
||||||
Discard,
|
Discard,
|
||||||
JQ { query: String },
|
JQ { query: String },
|
||||||
Regex { pattern: String },
|
Regex { pattern: String },
|
||||||
|
|
|
@ -13,16 +13,3 @@ pub struct PostWomanConfig {
|
||||||
// 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: indexmap::IndexMap<String, Endpoint>,
|
pub route: indexmap::IndexMap<String, Endpoint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum StringOr<T> {
|
|
||||||
Str(String),
|
|
||||||
T(T),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Default> Default for StringOr<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::T(T::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue