Skip to content

Commit 1781e74

Browse files
committed
query params escape; response save to file
1 parent f66f271 commit 1781e74

File tree

12 files changed

+434
-128
lines changed

12 files changed

+434
-128
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
9+
## [1.2.1] 2025-07-01
10+
### Added
11+
12+
* Global response state management system
13+
* Persistent response caching using `_G._http_client_state`
14+
* Response history with configurable size limit
15+
16+
* Enhanced file operations
17+
* New `write_file` utility function with error handling
18+
* Automatic directory creation for saves
19+
* Cross-platform path handling using Plenary
20+
21+
* Improved URL handling
22+
* Enhanced URL encoding for complex query parameters
23+
* Tested handling for Salesforce SOQL queries
24+
* Comprehensive character escaping
25+
26+
* Response saving features
27+
* New command `:HttpSaveResponse` for formatted response
28+
* Content-type based file extension detection
29+
* Keybindings:
30+
* `<leader>hs` - Save formatted response
31+
32+
833
## [1.2.0] 2024-12-30
934
### Added
1035
- Resopnse window management

doc/tags

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
:HttpCopyCurl http_client.txt /*:HttpCopyCurl*
2+
:HttpDryRun http_client.txt /*:HttpDryRun*
3+
:HttpEnv http_client.txt /*:HttpEnv*
4+
:HttpEnvFile http_client.txt /*:HttpEnvFile*
5+
:HttpRun http_client.txt /*:HttpRun*
6+
:HttpRunAll http_client.txt /*:HttpRunAll*
7+
:HttpStop http_client.txt /*:HttpStop*
8+
:HttpVerbose http_client.txt /*:HttpVerbose*
9+
http_client-commands http_client.txt /*http_client-commands*
10+
http_client-comments http_client.txt /*http_client-comments*
11+
http_client-config-keybindings http_client.txt /*http_client-config-keybindings*
12+
http_client-configuration http_client.txt /*http_client-configuration*
13+
http_client-contents http_client.txt /*http_client-contents*
14+
http_client-copy-curl http_client.txt /*http_client-copy-curl*
15+
http_client-default_env_file http_client.txt /*http_client-default_env_file*
16+
http_client-dry-run http_client.txt /*http_client-dry-run*
17+
http_client-environment-files http_client.txt /*http_client-environment-files*
18+
http_client-global-variables http_client.txt /*http_client-global-variables*
19+
http_client-http-versions http_client.txt /*http_client-http-versions*
20+
http_client-keybindings http_client.txt /*http_client-keybindings*
21+
http_client-no-environment http_client.txt /*http_client-no-environment*
22+
http_client-request_timeout http_client.txt /*http_client-request_timeout*
23+
http_client-response-handlers http_client.txt /*http_client-response-handlers*
24+
http_client-run http_client.txt /*http_client-run*
25+
http_client-select-env-file http_client.txt /*http_client-select-env-file*
26+
http_client-set-env http_client.txt /*http_client-set-env*
27+
http_client-split_direction http_client.txt /*http_client-split_direction*
28+
http_client-ssl-config http_client.txt /*http_client-ssl-config*
29+
http_client-stop http_client.txt /*http_client-stop*
30+
http_client-telescope http_client.txt /*http_client-telescope*
31+
http_client-usage http_client.txt /*http_client-usage*
32+
http_client-verbose http_client.txt /*http_client-verbose*
33+
http_client.txt http_client.txt /*http_client.txt*

lua/http_client/commands/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ local M = {}
33
M.request = require('http_client.commands.request')
44
M.select_env = require('http_client.commands.select_env')
55
M.utils = require('http_client.commands.utils')
6+
M.response = require('http_client.commands.response')
67

78
return M
89

lua/http_client/commands/request.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ M.stop_request = function()
6161
end
6262

6363
M.run_all = function()
64-
local verbose = vvv.get_verbose_mode()
64+
local verbose = vvv.get_verbose_mode()
6565
vvv.set_verbose_mode(verbose)
6666

6767
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
@@ -72,7 +72,8 @@ M.run_all = function()
7272
for _, request in ipairs(requests) do
7373
local env_needed = environment.env_variables_needed(request)
7474
if env_needed and not next(env) then
75-
table.insert(results, string.format("SKIP: %s %s - Environment variables needed but not set", request.method, request.url))
75+
table.insert(results,
76+
string.format("SKIP: %s %s - Environment variables needed but not set", request.method, request.url))
7677
else
7778
request = parser.replace_placeholders(request, env)
7879
local response = http_client.send_request_sync(request)

lua/http_client/commands/response.lua

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
local M = {}
2+
local state = require('http_client.state')
3+
local file_utils = require('http_client.utils.file_utils')
4+
5+
M.save_response = function(opts)
6+
local response = state.get_current_response()
7+
if not response then
8+
vim.notify("No response available to save", vim.log.levels.WARN)
9+
return
10+
end
11+
12+
local default_ext = response.content_type == "json" and ".json"
13+
or response.content_type == "xml" and ".xml"
14+
or response.content_type == "html" and ".html"
15+
or ".txt"
16+
17+
local content = opts and opts.raw and response.raw_body or response.formatted_body
18+
19+
vim.ui.input({
20+
prompt = "Save response as (default extension: " .. default_ext .. "): ",
21+
default = "response" .. default_ext,
22+
completion = "file"
23+
}, function(filename)
24+
if not filename then return end
25+
26+
-- Add default extension if none provided
27+
if not filename:match("%.%w+$") then
28+
filename = filename .. default_ext
29+
end
30+
31+
-- Write the file using our utility
32+
local success, err = file_utils.write_file(filename, content)
33+
34+
if success then
35+
vim.notify(string.format("Response saved to: %s (%s)",
36+
filename,
37+
opts and opts.raw and "raw" or "formatted"),
38+
vim.log.levels.INFO)
39+
else
40+
vim.notify("Failed to save response: " .. tostring(err), vim.log.levels.ERROR)
41+
end
42+
end)
43+
end
44+
45+
46+
return M
47+

lua/http_client/config.lua

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
local M = {}
22

33
M.defaults = {
4-
default_env_file = ".env.json",
5-
request_timeout = 30000, -- 30 seconds
6-
split_direction = "right",
7-
create_keybindings = true,
8-
keybindings = {
9-
select_env_file = "<leader>he",
10-
set_env = "<leader>hs",
11-
run_request = "<leader>hr",
12-
stop_request = "<leader>hx",
13-
dry_run = "<leader>hd",
14-
toggle_verbose = "<leader>hv",
15-
copy_curl = "<leader>hc",
16-
},
4+
default_env_file = ".env.json",
5+
request_timeout = 30000, -- 30 seconds
6+
split_direction = "right",
7+
create_keybindings = true,
8+
keybindings = {
9+
select_env_file = "<leader>hf",
10+
set_env = "<leader>he",
11+
run_request = "<leader>hr",
12+
stop_request = "<leader>hx",
13+
dry_run = "<leader>hd",
14+
toggle_verbose = "<leader>hv",
15+
copy_curl = "<leader>hc",
16+
save_response = "<header>hs",
17+
},
1718
}
1819

1920
M.options = {}
2021

2122
function M.setup(opts)
22-
opts = opts or {}
23-
M.options = vim.tbl_deep_extend("force", M.defaults, opts) or M.defaults
23+
opts = opts or {}
24+
M.options = vim.tbl_deep_extend("force", M.defaults, opts) or M.defaults
2425
end
2526

2627
function M.get(opt)
27-
return M.options[opt]
28+
return M.options[opt]
2829
end
2930

3031
return M
32+

lua/http_client/core/http_client.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local M = {}
22
local curl = require('plenary.curl')
33
local vvv = require('http_client.utils.verbose')
4+
local state = require('http_client.state')
45

56
local current_request = nil
67

@@ -105,7 +106,7 @@ local function prepare_response(request, response)
105106
formatted_body = format_xml(formatted_body)
106107
end
107108

108-
return {
109+
local pr = {
109110
formatted_body = formatted_body,
110111
headers = response.headers or {},
111112
status = response.status or "N/A",
@@ -118,6 +119,10 @@ local function prepare_response(request, response)
118119
test_name = request.test_name or "N/A",
119120
}
120121
}
122+
123+
state.store_response(pr)
124+
125+
return pr
121126
end
122127

123128
local function display_response(pr)

lua/http_client/core/parser.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
local M = {}
22
local environment = require('http_client.core.environment')
3+
local url_u = require('http_client.utils.url')
34

45
local function trim(s)
56
return s:match("^%s*(.-)%s*$")
@@ -198,6 +199,9 @@ M.replace_placeholders = function(request, env)
198199

199200
if request.url then
200201
request.url = replace(request.url)
202+
if (url_u.needs_encoding(request.url)) then
203+
request.url = url_u.encode_url(request.url)
204+
end
201205
end
202206
for k, v in pairs(request.headers) do
203207
request.headers[k] = replace(v)

0 commit comments

Comments
 (0)