Compare commits

...

5 commits

7 changed files with 108 additions and 48 deletions

48
.github/workflows/test.yml vendored Normal file
View 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

View file

@ -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>
```

View file

@ -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 {

View file

@ -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)

View file

@ -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"
} }

View file

@ -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 },

View file

@ -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())
}
}