Skip to content

feat: Add option for json env and allow manipulating env files during the session #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions doc/rest-nvim.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ FEATURES *rest-nvim-features*
- Run request under cursor
- Syntax highlight for http files and output
- Possibility of using environment variables in http files
- Set environment variables based on the response


===============================================================================
Expand Down Expand Up @@ -118,6 +119,9 @@ COMMANDS *rest-nvim-usage-commands*
Same as `RestNvim` but it returns the cURL command without executing the
request. Intended for debugging purposes.

- `:RestSelectEnv path/to/env`
Set the path to an env file.


===============================================================================
REQUESTS *rest-nvim-usage-requests*
Expand Down Expand Up @@ -151,6 +155,40 @@ These environment variables can be obtained from:
- File in the current working directory (env_file in config or '.env')
- System

Environment variables can be set in .env format or in json.

To change the environment for the session use :RestSelectEnv path/to/environment

Environment variables can be set dynamically from the response body.
(see rest-nvim-usage-dynamic-variables)


===============================================================================
RESPONSE SCRIPT *rest-nvim-response-script*

A lua script can be run after a request has completed. This script must below
the body and wrapped in {% script %}. A context table is avaliable in the
response script. The context table can be used to read the response and set
environment variables.

The context table:
`{`
` result = res,`
` pretty_print = vim.pretty_print,`
` json_decode = vim.fn.json_decode,`
` set_env = utils.set_env,`
`}`

Now environment variables can be set like so:

`GET https://jsonplaceholder.typicode.com/posts/3`
` `
`{%`
` `
`local body = context.json_decode(context.result.body)`
`context.set_env("postId", body.id)`
` `
`%}`

===============================================================================
DYNAMIC VARIABLES *rest-nvim-usage-dynamic-variables*
Expand Down
1 change: 1 addition & 0 deletions doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ rest-nvim-intro rest-nvim.txt /*rest-nvim-intro*
rest-nvim-issues rest-nvim.txt /*rest-nvim-issues*
rest-nvim-license rest-nvim.txt /*rest-nvim-license*
rest-nvim-quick-start rest-nvim.txt /*rest-nvim-quick-start*
rest-nvim-response-script rest-nvim.txt /*rest-nvim-response-script*
rest-nvim-usage rest-nvim.txt /*rest-nvim-usage*
rest-nvim-usage-commands rest-nvim.txt /*rest-nvim-usage-commands*
rest-nvim-usage-dynamic-variables rest-nvim.txt /*rest-nvim-usage-dynamic-variables*
Expand Down
19 changes: 17 additions & 2 deletions lua/rest-nvim/curl/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ M.get_or_create_buf = function()
return new_bufnr
end

local function create_callback(method, url)
local function create_callback(method, url, script_str)
return function(res)
if res.exit ~= 0 then
log.error("[rest.nvim] " .. utils.curl_error(res.exit))
Expand All @@ -68,6 +68,21 @@ local function create_callback(method, url)
end
end

if script_str ~= nil then
local context = {
result = res,
pretty_print = vim.pretty_print,
json_decode = vim.fn.json_decode,
set_env = utils.set_env,
}
local env = { context = context }
setmetatable(env, { __index = _G })
local f = load(script_str, nil, "bt", env)
if f ~= nil then
f()
end
end

if config.get("result").show_url then
--- Add metadata into the created buffer (status code, date, etc)
-- Request statement (METHOD URL)
Expand Down Expand Up @@ -212,7 +227,7 @@ M.curl_cmd = function(opts)
vim.api.nvim_echo({ { "[rest.nvim] Request preview:\n", "Comment" }, { curl_cmd } }, false, {})
return
else
opts.callback = vim.schedule_wrap(create_callback(opts.method, opts.url))
opts.callback = vim.schedule_wrap(create_callback(opts.method, opts.url, opts.script_str))
curl[opts.method](opts)
end
end
Expand Down
14 changes: 12 additions & 2 deletions lua/rest-nvim/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ end
rest.run_request = function(req, opts)
local result = req
opts = vim.tbl_deep_extend(
"force", -- use value from rightmost map
{verbose = false}, -- defaults
"force", -- use value from rightmost map
{ verbose = false }, -- defaults
opts or {}
)

Expand All @@ -74,6 +74,7 @@ rest.run_request = function(req, opts)
bufnr = result.bufnr,
start_line = result.start_line,
end_line = result.end_line,
script_str = result.script_str,
}

if not opts.verbose then
Expand Down Expand Up @@ -118,4 +119,13 @@ end

rest.request = request

rest.select_env = function(path)
if path ~= nil then
vim.validate({ path = { path, "string" } })
config.set({ env_file = path })
else
print("No path given")
end
end

return rest
73 changes: 58 additions & 15 deletions lua/rest-nvim/request/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ local function get_importfile_name(bufnr, start_line, stop_line)
local fileimport_spliced
fileimport_line = vim.api.nvim_buf_get_lines(bufnr, import_line - 1, import_line, false)
fileimport_string =
string.gsub(fileimport_line[1], "<", "", 1):gsub("^%s+", ""):gsub("%s+$", "")
string.gsub(fileimport_line[1], "<", "", 1):gsub("^%s+", ""):gsub("%s+$", "")
fileimport_spliced = utils.replace_vars(fileimport_string)
if path:new(fileimport_spliced):is_absolute() then
return fileimport_spliced
Expand Down Expand Up @@ -56,14 +56,19 @@ local function get_body(bufnr, start_line, stop_line, has_json)
end

local body = ""
local vars = utils.read_variables()
-- nvim_buf_get_lines is zero based and end-exclusive
-- but start_line and stop_line are one-based and inclusive
-- magically, this fits :-) start_line is the CRLF between header and body
-- which should not be included in the body, stop_line is the last line of the body
for _, line in ipairs(lines) do
-- stop if a script opening tag is found
if line:find("{%%") then
break
end
-- Ignore commented lines with and without indent
if not utils.contains_comments(line) then
body = body .. utils.replace_vars(line)
body = body .. utils.replace_vars(line, vars)
end
end

Expand All @@ -80,13 +85,46 @@ local function get_body(bufnr, start_line, stop_line, has_json)
json_body[key] = vim.fn.json_encode(val)
end
end
return json_body
return vim.fn.json_encode(json_body)
end
end


return body
end

local function get_response_script(bufnr, start_line, stop_line)
local all_lines = vim.api.nvim_buf_get_lines(bufnr, start_line, stop_line, false)
-- Check if there is a script
local script_start_rel
for i, line in ipairs(all_lines) do
-- stop if a script opening tag is found
if line:find("{%%") then
script_start_rel = i
break
end
end

if script_start_rel == nil then
return nil
end

-- Convert the relative script line number to the line number of the buffer
local script_start = start_line + script_start_rel - 1

local script_lines = vim.api.nvim_buf_get_lines(bufnr, script_start, stop_line, false)
local script_str = ""

for _, line in ipairs(script_lines) do
script_str = script_str .. line .. "\n"
if line:find("%%}") then
break
end
end

return script_str:match("{%%(.-)%%}")
end

-- is_request_line checks if the given line is a http request line according to RFC 2616
local function is_request_line(line)
local http_methods = { "GET", "POST", "PUT", "PATCH", "DELETE" }
Expand Down Expand Up @@ -202,7 +240,8 @@ local function end_request(bufnr, linenumber)
end
utils.move_cursor(bufnr, linenumber)

local next = vim.fn.search("^GET\\|^POST\\|^PUT\\|^PATCH\\|^DELETE", "cn", vim.fn.line("$"))
local next = vim.fn.search("^GET\\|^POST\\|^PUT\\|^PATCH\\|^DELETE\\^###\\", "cn",
vim.fn.line("$"))

-- restore cursor position
utils.move_cursor(bufnr, oldlinenumber)
Expand Down Expand Up @@ -296,24 +335,28 @@ M.buf_get_request = function(bufnr, curpos)
content_type:find("application/[^ ]*json")
)

local script_str = get_response_script(bufnr, headers_end, end_line)


if config.get("jump_to_request") then
utils.move_cursor(bufnr, start_line)
else
utils.move_cursor(bufnr, curpos[2], curpos[3])
end

return true,
{
method = parsed_url.method,
url = parsed_url.url,
http_version = parsed_url.http_version,
headers = headers,
raw = curl_args,
body = body,
bufnr = bufnr,
start_line = start_line,
end_line = end_line,
}
{
method = parsed_url.method,
url = parsed_url.url,
http_version = parsed_url.http_version,
headers = headers,
raw = curl_args,
body = body,
bufnr = bufnr,
start_line = start_line,
end_line = end_line,
script_str = script_str
}
end

M.print_request = function(req)
Expand Down
86 changes: 76 additions & 10 deletions lua/rest-nvim/utils/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,41 @@ M.move_cursor = function(bufnr, line, column)
end)
end

M.set_env = function(key, value)
local variables = M.get_env_variables()
variables[key] = value
M.write_env_file(variables)
end

M.write_env_file = function(variables)
local env_file = "/" .. (config.get("env_file") or ".env")

-- Directories to search for env files
local env_file_paths = {
-- current working directory
vim.fn.getcwd() .. env_file,
-- directory of the currently opened file
vim.fn.expand("%:p:h") .. env_file,
}

-- If there's an env file in the current working dir
for _, env_file_path in ipairs(env_file_paths) do
if M.file_exists(env_file_path) then
local file = io.open(env_file_path, "w+")
if file ~= nil then
if string.match(env_file_path, "(.-)%.json$") then
file:write(vim.fn.json_encode(variables))
else
for key, value in pairs(variables) do
file:write(key .. "=" .. value .. "\n")
end
end
file:close()
end
end
end
end

M.uuid = function()
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
return string.gsub(template, "[xy]", function(c)
Expand Down Expand Up @@ -43,10 +78,8 @@ M.read_file = function(file)
return lines
end

-- get_variables Reads the environment variables found in the env_file option
-- (defualt: .env) specified in configuration or from the files being read
-- with variables beginning with @ and returns a table with the variables
M.get_variables = function()
-- reads the variables contained in the current file
M.get_file_variables = function()
local variables = {}

-- If there is a line at the beginning with @ first
Expand All @@ -64,7 +97,11 @@ M.get_variables = function()
end
end
end

return variables
end
-- Gets the variables from the currently selected env_file
M.get_env_variables = function()
local variables = {}
local env_file = "/" .. (config.get("env_file") or ".env")

-- Directories to search for env files
Expand All @@ -78,12 +115,39 @@ M.get_variables = function()
-- If there's an env file in the current working dir
for _, env_file_path in ipairs(env_file_paths) do
if M.file_exists(env_file_path) then
for line in io.lines(env_file_path) do
local vars = M.split(line, "%s*=%s*", 1)
variables[vars[1]] = vars[2]
if string.match(env_file_path, "(.-)%.json$") then
local f = io.open(env_file_path, "r")
if f ~= nil then
local json_vars = f:read("*all")
variables = vim.fn.json_decode(json_vars)
f:close()
end
else
for line in io.lines(env_file_path) do
local vars = M.split(line, "%s*=%s*", 1)
variables[vars[1]] = vars[2]
end
end
end
end
return variables
end

-- get_variables Reads the environment variables found in the env_file option
-- (defualt: .env) specified in configuration or from the files being read
-- with variables beginning with @ and returns a table with the variables
M.get_variables = function()
local variables = {}
local file_variables = M.get_file_variables()
local env_variables = M.get_env_variables()

for k, v in pairs(file_variables) do
variables[k] = v
end

for k, v in pairs(env_variables) do
variables[k] = v
end

-- For each variable name
for name, _ in pairs(variables) do
Expand Down Expand Up @@ -165,8 +229,10 @@ end
-- replace_vars replaces the env variables fields in the provided string
-- with the env variable value
-- @param str Where replace the placers for the env variables
M.replace_vars = function(str)
local vars = M.read_variables()
M.replace_vars = function(str, vars)
if vars == nil then
vars = M.read_variables()
end
-- remove $dotenv tags, which are used by the vscode rest client for cross compatibility
str = str:gsub("%$dotenv ", ""):gsub("%$DOTENV ", "")

Expand Down
Loading