From baf4a7ef8d56fd9484e6872e5e906a9895a01b29 Mon Sep 17 00:00:00 2001 From: Oleksii Rodionov Date: Tue, 7 Oct 2025 10:17:45 +0300 Subject: [PATCH 1/2] JSON Parsing feature --- Cargo.lock | 120 +++++++++++++++---------------- Cargo.toml | 2 +- README.md | 28 ++++++++ src/cli.rs | 9 +++ src/commands.rs | 17 +++-- src/display.rs | 188 +++++++++++++++++++++++++++++++++++++----------- src/main.rs | 12 +++- 7 files changed, 266 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 291f4bf..0e6eec2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,7 +106,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -169,7 +169,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -237,7 +237,7 @@ dependencies = [ "libc", "once_cell", "unicode-width", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -313,7 +313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1001,7 +1001,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -1180,7 +1180,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1243,7 +1243,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1483,7 +1483,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1728,9 +1728,9 @@ checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unit-prefix" @@ -1944,7 +1944,7 @@ dependencies = [ "url", "urlencoding", "uuid", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1953,27 +1953,27 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "windows-core" -version = "0.62.1" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] name = "windows-implement" -version = "0.60.1" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -1982,9 +1982,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.2" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -1999,9 +1999,9 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" @@ -2025,11 +2025,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2043,11 +2043,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2074,16 +2074,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.4", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2104,19 +2104,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.0", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2127,9 +2127,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -2139,9 +2139,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -2151,9 +2151,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -2163,9 +2163,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -2175,9 +2175,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -2187,9 +2187,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -2199,9 +2199,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -2211,9 +2211,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" diff --git a/Cargo.toml b/Cargo.toml index c5cd80a..051caf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ uuid = { version = "^1.0", features = ["v4"] } chrono = { version = "^0.4", features = ["serde"] } colored = "^3.0" indicatif = "^0.18" -windows-sys = { version = "0.61.1", features = ["Win32_Foundation", "Win32_System_Console"] } +windows-sys = { version = "0.61.2", features = ["Win32_Foundation", "Win32_System_Console"] } anyhow = "^1.0" toml = "^0.9" url = "^2.4" diff --git a/README.md b/README.md index 0000290..4d7ef9a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A fast, efficient command-line tool for webhook testing and monitoring built in - **Request Logs**: View historical webhook requests - **Detailed Inspection**: Show full request details including headers and body - **Method Filtering**: Filter requests by HTTP method +- **JSON Path Parsing**: Extract and display specific fields from JSON request bodies - **Colorized Output**: Beautiful, readable colored terminal output ## Configuration @@ -107,6 +108,9 @@ webhook monitor --token YOUR_TOKEN --full-body # Show request headers webhook monitor --token YOUR_TOKEN --show-headers +# Parse and display only specific JSON fields from request bodies +webhook monitor --token YOUR_TOKEN --parse "/user/name" --parse "/data/id" + # Combine multiple options webhook monitor --token YOUR_TOKEN --full-body --show-headers --method POST ``` @@ -128,6 +132,9 @@ webhook logs --token YOUR_TOKEN --full-body # Show logs with headers webhook logs --token YOUR_TOKEN --show-headers +# Parse specific JSON fields from logged requests +webhook logs --token YOUR_TOKEN --parse "/event/type" --parse "/user/email" + # Combine options for detailed view webhook logs --token YOUR_TOKEN --full-body --show-headers ``` @@ -135,6 +142,9 @@ webhook logs --token YOUR_TOKEN --full-body --show-headers ### Show Request Details ```bash webhook show --token YOUR_TOKEN --request-id REQUEST_ID + +# Parse specific JSON fields from the request body +webhook show --token YOUR_TOKEN --request-id REQUEST_ID --parse "/user/profile" --parse "/metadata/timestamp" ``` ## Command Reference @@ -152,6 +162,7 @@ Monitors webhook requests in real-time. - `-m, --method ` - Filter by HTTP method (GET, POST, PUT, DELETE, PATCH) - `--full-body` - Show full request body with proper formatting (JSON, form data, etc.) - `--show-headers` - Show request headers +- `--parse ` - Parse and display only specific JSON paths from the request body (e.g., "/user/name", "/data/items/0") ### `webhook logs` Shows historical webhook requests. @@ -162,6 +173,7 @@ Shows historical webhook requests. - `-m, --method ` - Filter by HTTP method - `--full-body` - Show full request body with proper formatting - `--show-headers` - Show request headers +- `--parse ` - Parse and display only specific JSON paths from the request body (e.g., "/user/name", "/data/items/0") ### `webhook show` Shows detailed information for a specific request. @@ -169,6 +181,7 @@ Shows detailed information for a specific request. **Options:** - `-t, --token ` - Webhook token (required) - `-r, --request-id ` - Request ID to show details for (required) +- `--parse ` - Parse and display only specific JSON paths from the request body (e.g., "/user/name", "/data/items/0") ## Examples @@ -234,6 +247,21 @@ Press Ctrl+C to quit ──────────────────────────────────────────────────────────────────────────────── ``` +### Parsed JSON Fields +When using `--parse` with JSON request bodies: +``` +📄 REQUEST BODY +────────────────────────────── +🧩 PARSED JSON FIELDS +/user_id: +12345 + +/metadata/ip: +"192.168.1.1" + +/nonexistent/path: null (path not found) +``` + ### Request Details ``` 🔍 Fetching request details... diff --git a/src/cli.rs b/src/cli.rs index dfc3bd2..1f7c94a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -37,6 +37,9 @@ pub enum Commands { /// Show request headers #[arg(long)] show_headers: bool, + /// Parse and display only specific JSON paths from the request body (e.g., "/user/name", "/data/items/0") + #[arg(long, value_name = "PATH")] + parse: Vec, }, /// Show request logs for a token Logs { @@ -55,6 +58,9 @@ pub enum Commands { /// Show request headers #[arg(long)] show_headers: bool, + /// Parse and display only specific JSON paths from the request body (e.g., "/user/name", "/data/items/0") + #[arg(long, value_name = "PATH")] + parse: Vec, }, /// Show details of a specific request Show { @@ -64,5 +70,8 @@ pub enum Commands { /// Request ID to show details for #[arg(short, long)] request_id: String, + /// Parse and display only specific JSON paths from the request body (e.g., "/user/name", "/data/items/0") + #[arg(long, value_name = "PATH")] + parse: Vec, }, } diff --git a/src/commands.rs b/src/commands.rs index a10af98..3bfe47b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -46,6 +46,7 @@ pub async fn monitor_requests( method_filter: Option<&str>, full_body: bool, show_headers: bool, + parse_paths: &[String], ) -> Result<()> { println!("{}", "Starting webhook monitor...".bright_green().bold()); println!("Token: {}", token.bright_white()); @@ -96,8 +97,8 @@ pub async fn monitor_requests( if show_headers { print_request_headers(request); } - if full_body { - print_full_request_body(request); + if full_body || !parse_paths.is_empty() { + print_full_request_body(request, parse_paths, full_body); println!(); // Add spacing between requests when showing full body } last_seen_ids.insert(request.id.clone()); @@ -120,8 +121,8 @@ pub async fn monitor_requests( if show_headers { print_request_headers(request); } - if full_body { - print_full_request_body(request); + if full_body || !parse_paths.is_empty() { + print_full_request_body(request, parse_paths, full_body); } println!("{}", "─".repeat(80).bright_black()); last_seen_ids.insert(request.id.clone()); @@ -145,6 +146,7 @@ pub async fn show_logs( method_filter: Option<&str>, full_body: bool, show_headers: bool, + parse_paths: &[String], ) -> Result<()> { println!("{}", "Fetching webhook logs...".bright_blue().bold()); @@ -190,8 +192,8 @@ pub async fn show_logs( if show_headers { print_request_headers(request); } - if full_body { - print_full_request_body(request); + if full_body || !parse_paths.is_empty() { + print_full_request_body(request, parse_paths, full_body); println!(); // Add spacing between requests when showing full body } } @@ -209,6 +211,7 @@ pub async fn show_request_details( client: &WebhookClient, token: &str, request_id: &str, + parse_paths: &[String], ) -> Result<()> { println!("{}", "Fetching request details...".bright_blue().bold()); @@ -219,7 +222,7 @@ pub async fn show_request_details( .find(|req| req.id == request_id) .with_context(|| format!("Request with ID {} not found", request_id))?; - print_request_details(&request); + print_request_details(&request, parse_paths, true); Ok(()) } diff --git a/src/display.rs b/src/display.rs index 372fb00..0973226 100644 --- a/src/display.rs +++ b/src/display.rs @@ -47,42 +47,111 @@ pub fn print_request_headers(request: &WebhookRequest) { } } -pub fn print_full_request_body(request: &WebhookRequest) { - println!("{}", "REQUEST BODY".bright_cyan().bold()); - println!("{}", "─".repeat(30).bright_black()); - +pub fn print_full_request_body(request: &WebhookRequest, parse_paths: &[String], full_body: bool) { if let Some(body) = &request.message_object.body { if body.trim().is_empty() { - println!("{}", "(empty)".bright_black()); + if !parse_paths.is_empty() { + // When parsing is enabled but body is empty, show parsed fields section with empty message + println!("{}", "PARSED JSON FIELDS".bright_green().bold()); + println!("{}", "(empty body)".bright_black()); + } else { + // Original behavior with REQUEST BODY header + println!("{}", "REQUEST BODY".bright_cyan().bold()); + println!("{}", "─".repeat(30).bright_black()); + println!("{}", "(empty)".bright_black()); + } } else { - // Try to pretty-print JSON with syntax highlighting - match serde_json::from_str::(body) { - Ok(json) => { - let pretty_json = serde_json::to_string_pretty(&json).unwrap(); - highlight_json(&pretty_json); - println!(); // Add newline after the highlighted JSON - } - Err(_) => { - // Not JSON, check if it's form data or other structured format - if body.contains('&') - && (body.contains('=') - || body.starts_with("application/x-www-form-urlencoded")) - { - // Try to format form data nicely - println!("{}", format_form_data(body).bright_white()); - } else { - // Raw text with proper line breaks + // Body is not empty + if !parse_paths.is_empty() { + // Show parsed fields + match serde_json::from_str::(body) { + Ok(json) => { + println!("{}", "PARSED JSON FIELDS".bright_green().bold()); + for path in parse_paths { + match json.pointer(path) { + Some(value) => { + println!("{}:", path.bright_blue()); + let pretty_value = serde_json::to_string_pretty(value).unwrap(); + highlight_json(&pretty_value); + println!(); + } + None => { + println!( + "{}: {} (path not found)", + path.bright_blue(), + "null".bright_red() + ); + } + } + } + + // If full_body is also true, show the full body after parsed fields + if full_body { + println!("{}", "REQUEST BODY".bright_cyan().bold()); + println!("{}", "─".repeat(30).bright_black()); + let pretty_json = serde_json::to_string_pretty(&json).unwrap(); + highlight_json(&pretty_json); + println!(); // Add newline after the highlighted JSON + } + } + Err(_) => { + println!( + "{}", + "Body is not valid JSON, cannot parse paths".bright_red() + ); println!("{}", body.bright_white()); + + // If full_body is also true, still show the body + if full_body { + println!("{}", "REQUEST BODY".bright_cyan().bold()); + println!("{}", "─".repeat(30).bright_black()); + println!("{}", body.bright_white()); + } + } + } + } else { + // Original behavior with REQUEST BODY header + println!("{}", "REQUEST BODY".bright_cyan().bold()); + println!("{}", "─".repeat(30).bright_black()); + + // Try to pretty-print JSON with syntax highlighting + match serde_json::from_str::(body) { + Ok(json) => { + let pretty_json = serde_json::to_string_pretty(&json).unwrap(); + highlight_json(&pretty_json); + println!(); // Add newline after the highlighted JSON + } + Err(_) => { + // Not JSON, check if it's form data or other structured format + if body.contains('&') + && (body.contains('=') + || body.starts_with("application/x-www-form-urlencoded")) + { + // Try to format form data nicely + println!("{}", format_form_data(body).bright_white()); + } else { + // Raw text with proper line breaks + println!("{}", body.bright_white()); + } } } } } } else { - println!("{}", "(no body)".bright_black()); + if !parse_paths.is_empty() { + // When parsing is enabled but no body, show parsed fields section with no body message + println!("{}", "PARSED JSON FIELDS".bright_green().bold()); + println!("{}", "(no body)".bright_black()); + } else { + // Original behavior with REQUEST BODY header + println!("{}", "REQUEST BODY".bright_cyan().bold()); + println!("{}", "─".repeat(30).bright_black()); + println!("{}", "(no body)".bright_black()); + } } } -pub fn print_request_details(request: &WebhookRequest) { +pub fn print_request_details(request: &WebhookRequest, parse_paths: &[String], _full_body: bool) { println!("{}", "REQUEST DETAILS".bright_green().bold()); println!("{}", "═".repeat(50).bright_black()); @@ -135,26 +204,65 @@ pub fn print_request_details(request: &WebhookRequest) { } // Body - println!("{}", "REQUEST BODY".bright_cyan().bold()); - println!("{}", "─".repeat(30).bright_black()); - if let Some(body) = &request.message_object.body { - if body.trim().is_empty() { - println!("{}", "(empty)".bright_black()); - } else { - // Try to pretty-print JSON with syntax highlighting - match serde_json::from_str::(body) { - Ok(json) => { - let pretty_json = serde_json::to_string_pretty(&json).unwrap(); - highlight_json(&pretty_json); - println!(); // Add newline after the highlighted JSON - } - Err(_) => { - println!("{}", body.bright_white()); + if !parse_paths.is_empty() { + // When parsing is enabled, skip the "REQUEST BODY" header + if let Some(body) = &request.message_object.body { + if !body.trim().is_empty() { + // Parse and display only specific JSON paths + match serde_json::from_str::(body) { + Ok(json) => { + println!("{}", "PARSED JSON FIELDS".bright_green().bold()); + for path in parse_paths { + match json.pointer(path) { + Some(value) => { + println!("{}:", path.bright_blue()); + let pretty_value = serde_json::to_string_pretty(value).unwrap(); + highlight_json(&pretty_value); + println!(); + } + None => { + println!( + "{}: {} (path not found)", + path.bright_blue(), + "null".bright_red() + ); + } + } + } + } + Err(_) => { + println!( + "{}", + "Body is not valid JSON, cannot parse paths".bright_red() + ); + println!("{}", body.bright_white()); + } } } } } else { - println!("{}", "(no body)".bright_black()); + // Original behavior with REQUEST BODY header + println!("{}", "REQUEST BODY".bright_cyan().bold()); + println!("{}", "─".repeat(30).bright_black()); + if let Some(body) = &request.message_object.body { + if body.trim().is_empty() { + println!("{}", "(empty)".bright_black()); + } else { + // Original behavior: Try to pretty-print JSON with syntax highlighting + match serde_json::from_str::(body) { + Ok(json) => { + let pretty_json = serde_json::to_string_pretty(&json).unwrap(); + highlight_json(&pretty_json); + println!(); // Add newline after the highlighted JSON + } + Err(_) => { + println!("{}", body.bright_white()); + } + } + } + } else { + println!("{}", "(no body)".bright_black()); + } } } diff --git a/src/main.rs b/src/main.rs index b3ccd88..6065a30 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,7 @@ async fn main() -> Result<()> { method, full_body, show_headers, + parse, } => { let token = match token { Some(t) => t, @@ -74,6 +75,7 @@ async fn main() -> Result<()> { method.as_deref(), full_body, show_headers, + &parse, ) .await?; } @@ -83,6 +85,7 @@ async fn main() -> Result<()> { method, full_body, show_headers, + parse, } => { show_logs( &client, @@ -92,12 +95,17 @@ async fn main() -> Result<()> { method.as_deref(), full_body, show_headers, + &parse, ) .await?; } - Commands::Show { token, request_id } => { - show_request_details(&client, &token, &request_id).await?; + Commands::Show { + token, + request_id, + parse, + } => { + show_request_details(&client, &token, &request_id, &parse).await?; } } From 35ab5132dd158d4e22b79edd8abe0f109e4c00b4 Mon Sep 17 00:00:00 2001 From: Oleksii Rodionov Date: Tue, 7 Oct 2025 16:41:51 +0300 Subject: [PATCH 2/2] fixes --- README.md | 2 -- src/commands.rs | 1 + src/display.rs | 91 +++++++++++++++++++++++-------------------------- 3 files changed, 43 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 4d7ef9a..d6d8d43 100644 --- a/README.md +++ b/README.md @@ -250,8 +250,6 @@ Press Ctrl+C to quit ### Parsed JSON Fields When using `--parse` with JSON request bodies: ``` -📄 REQUEST BODY -────────────────────────────── 🧩 PARSED JSON FIELDS /user_id: 12345 diff --git a/src/commands.rs b/src/commands.rs index 3bfe47b..963a533 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -138,6 +138,7 @@ pub async fn monitor_requests( } } +#[allow(clippy::too_many_arguments)] pub async fn show_logs( client: &WebhookClient, config: &Config, diff --git a/src/display.rs b/src/display.rs index 0973226..aac1d5c 100644 --- a/src/display.rs +++ b/src/display.rs @@ -137,17 +137,15 @@ pub fn print_full_request_body(request: &WebhookRequest, parse_paths: &[String], } } } + } else if !parse_paths.is_empty() { + // When parsing is enabled but no body, show parsed fields section with no body message + println!("{}", "PARSED JSON FIELDS".bright_green().bold()); + println!("{}", "(no body)".bright_black()); } else { - if !parse_paths.is_empty() { - // When parsing is enabled but no body, show parsed fields section with no body message - println!("{}", "PARSED JSON FIELDS".bright_green().bold()); - println!("{}", "(no body)".bright_black()); - } else { - // Original behavior with REQUEST BODY header - println!("{}", "REQUEST BODY".bright_cyan().bold()); - println!("{}", "─".repeat(30).bright_black()); - println!("{}", "(no body)".bright_black()); - } + // Original behavior with REQUEST BODY header + println!("{}", "REQUEST BODY".bright_cyan().bold()); + println!("{}", "─".repeat(30).bright_black()); + println!("{}", "(no body)".bright_black()); } } @@ -204,51 +202,13 @@ pub fn print_request_details(request: &WebhookRequest, parse_paths: &[String], _ } // Body - if !parse_paths.is_empty() { - // When parsing is enabled, skip the "REQUEST BODY" header - if let Some(body) = &request.message_object.body { - if !body.trim().is_empty() { - // Parse and display only specific JSON paths - match serde_json::from_str::(body) { - Ok(json) => { - println!("{}", "PARSED JSON FIELDS".bright_green().bold()); - for path in parse_paths { - match json.pointer(path) { - Some(value) => { - println!("{}:", path.bright_blue()); - let pretty_value = serde_json::to_string_pretty(value).unwrap(); - highlight_json(&pretty_value); - println!(); - } - None => { - println!( - "{}: {} (path not found)", - path.bright_blue(), - "null".bright_red() - ); - } - } - } - } - Err(_) => { - println!( - "{}", - "Body is not valid JSON, cannot parse paths".bright_red() - ); - println!("{}", body.bright_white()); - } - } - } - } - } else { - // Original behavior with REQUEST BODY header + if parse_paths.is_empty() { println!("{}", "REQUEST BODY".bright_cyan().bold()); println!("{}", "─".repeat(30).bright_black()); if let Some(body) = &request.message_object.body { if body.trim().is_empty() { println!("{}", "(empty)".bright_black()); } else { - // Original behavior: Try to pretty-print JSON with syntax highlighting match serde_json::from_str::(body) { Ok(json) => { let pretty_json = serde_json::to_string_pretty(&json).unwrap(); @@ -263,6 +223,39 @@ pub fn print_request_details(request: &WebhookRequest, parse_paths: &[String], _ } else { println!("{}", "(no body)".bright_black()); } + } else if let Some(body) = &request.message_object.body + && !body.trim().is_empty() + { + // Parse and display only specific JSON paths + match serde_json::from_str::(body) { + Ok(json) => { + println!("{}", "PARSED JSON FIELDS".bright_green().bold()); + for path in parse_paths { + match json.pointer(path) { + Some(value) => { + println!("{}:", path.bright_blue()); + let pretty_value = serde_json::to_string_pretty(value).unwrap(); + highlight_json(&pretty_value); + println!(); + } + None => { + println!( + "{}: {} (path not found)", + path.bright_blue(), + "null".bright_red() + ); + } + } + } + } + Err(_) => { + println!( + "{}", + "Body is not valid JSON, cannot parse paths".bright_red() + ); + println!("{}", body.bright_white()); + } + } } }