Skip to content

Commit

Permalink
Merge pull request #93 from mistweaverco/feat/vscode-request-variables
Browse files Browse the repository at this point in the history
feat(parser): named-requests + request-variables + `replay()`
  • Loading branch information
gorillamoe authored Aug 5, 2024
2 parents db8d08b + 30752aa commit 6f7c5dc
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 13 deletions.
4 changes: 4 additions & 0 deletions docs/docs/usage/public-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ All public methods are available via the `kulala` module.

`require('kulala').run()` runs the current request.

### replay

`require('kulala').replay()` replays the last run request.

### copy

`require('kulala').copy()` copies the current request
Expand Down
76 changes: 76 additions & 0 deletions docs/docs/usage/request-variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Request Variables

The definition syntax of request variables is just like a single-line comment,
and follows `# @name REQUEST_NAME` just as metadata.

```http
POST https://httpbin.org/post HTTP/1.1
Content-Type: application/x-www-form-urlencoded
# @name THIS_IS_AN_EXAMPLE_REQUEST_NAME
name=foo&password=bar
```

You can think of request variable as attaching a name metadata to the underlying request,
and this kind of requests can be called with Named Request,
Other requests can use `THIS_IS_AN_EXAMPLE_REQUEST_NAME` as an
identifier to reference the expected part of the named request or its latest response.

**NOTE:** If you want to refer the response of a named request,
you need to manually trigger the named request to retrieve its response first,
otherwise the plain text of
variable reference like `{{THIS_IS_AN_EXAMPLE_REQUEST_NAME.response.body.$.id}}` will be sent instead.

The reference syntax of a request variable is a bit more complex than other kinds of custom variables.

## Request Variable Reference Syntax

The request variable reference syntax follows `{{REQUEST_NAME.(response|request).(body|headers).(*|JSONPath|XPath|Header Name)}}`.

You have two reference part choices of the `response` or `request`: `body` and `headers`.

For `body` part, you can use JSONPath and XPath to extract specific property or attribute.

## Example

if a JSON response returns `body` `{"id": "mock"}`, you can set the JSONPath part to `$.id` to reference the `id`.

For `headers` part, you can specify the header name to extract the header value.

The header name is case-sensitive for `response` part, and all lower-cased for `request` part.

If the *JSONPath* or *XPath* of `body`, or *Header Name* of `headers` can't be resolved,
the plain text of variable reference will be sent instead.
And in this case,
diagnostic information will be displayed to help you to inspect this.

Below is a sample of request variable definitions and references in an http file.

```http
POST https://httpbin.org/post HTTP/1.1
accept: application/json
# @name REQUEST_ONE
{
"token": "foobar"
}
###
POST https://httpbin.org/post HTTP/1.1
accept: application/json
# @name REQUEST_TWO
{
"token": "{{REQUEST_ONE.response.body.$.json.token}}"
}
###
POST https://httpbin.org/post HTTP/1.1
accept: application/json
{
"date_header": "{{REQUEST_TWO.response.headers['Date']}}"
}
```
4 changes: 3 additions & 1 deletion lua/kulala/cmd/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ M.run = function(result, callback)
end
for _, metadata in ipairs(result.metadata) do
if metadata then
if metadata.name == "env-json-key" then
if metadata.name == "name" then
INT_PROCESSING.set_env_for_named_request(metadata.value, body)
elseif metadata.name == "env-json-key" then
INT_PROCESSING.env_json_key(metadata.value, body)
elseif metadata.name == "env-header-key" then
INT_PROCESSING.env_header_key(metadata.value)
Expand Down
7 changes: 7 additions & 0 deletions lua/kulala/db/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
local M = {}

M.data = {
env = {},
}

return M
3 changes: 2 additions & 1 deletion lua/kulala/external_processing/init.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local FS = require("kulala.utils.fs")
local DB = require("kulala.db")

local M = {}

Expand All @@ -20,7 +21,7 @@ M.env_stdin_cmd = function(cmdstring, contents)
vim.notify("env_stdin_cmd --> Command failed: " .. cmd[2] .. ".", vim.log.levels.ERROR)
return
else
vim.env[env_name] = res
DB.data.env[env_name] = res
end
end

Expand Down
2 changes: 1 addition & 1 deletion lua/kulala/globals/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local FS = require("kulala.utils.fs")

local M = {}

M.VERSION = "2.8.2"
M.VERSION = "2.9.0"
M.UI_ID = "kulala://ui"
M.HEADERS_FILE = FS.get_plugin_tmp_dir() .. "/headers.txt"
M.BODY_FILE = FS.get_plugin_tmp_dir() .. "/body.txt"
Expand Down
4 changes: 4 additions & 0 deletions lua/kulala/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ M.run = function()
UI:open()
end

M.replay = function()
UI:replay()
end

M.copy = function()
UI:copy()
end
Expand Down
34 changes: 29 additions & 5 deletions lua/kulala/internal_processing/init.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local FS = require("kulala.utils.fs")
local GLOBALS = require("kulala.globals")
local DB = require("kulala.db")
local M = {}

-- Function to access a nested key in a table dynamically
Expand All @@ -22,26 +23,49 @@ local get_headers_as_table = function()
for _, header in ipairs(lines) do
if header:find(":") ~= nil then
local kv = vim.split(header, ":")
local key = kv[1]:lower()
local key = kv[1]
-- the value should be everything after the first colon
-- but we can't use slice and join because the value might contain colons
local value = header:sub(#key + 2)
headers_table[key] = value
headers_table[key] = vim.trim(value)
end
end
return headers_table
end

M.env_header_key = function(cmd)
local get_lower_headers_as_table = function()
local headers = get_headers_as_table()
local headers_table = {}
for key, value in pairs(headers) do
headers_table[key:lower()] = value
end
return headers_table
end

M.set_env_for_named_request = function(name, body)
local named_request = {
response = {
headers = get_headers_as_table(),
body = body,
},
request = {
headers = DB.data.current_request.headers,
body = DB.data.current_request.body,
},
}
DB.data.env[name] = named_request
end

M.env_header_key = function(cmd)
local headers = get_lower_headers_as_table()
local kv = vim.split(cmd, " ")
local header_key = kv[2]
local variable_name = kv[1]
local value = headers[header_key:lower()]
if value == nil then
vim.notify("env-header-key --> Header not found.", vim.log.levels.ERROR)
else
vim.fn.setenv(variable_name, value)
DB.data.env[variable_name] = value
end
end

Expand All @@ -52,7 +76,7 @@ M.env_json_key = function(cmd, body)
else
local kv = vim.split(cmd, " ")
local value = get_nested_value(json, kv[2])
vim.fn.setenv(kv[1], value)
DB.data.env[kv[1]] = value
end
end

Expand Down
4 changes: 4 additions & 0 deletions lua/kulala/parser/env.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local FS = require("kulala.utils.fs")
local GLOBAL_STORE = require("kulala.global_store")
local DYNAMIC_VARS = require("kulala.parser.dynamic_vars")
local DB = require("kulala.db")

local M = {}

Expand Down Expand Up @@ -40,6 +41,9 @@ M.get_env = function()
end
end
end
for key, value in pairs(DB.data.env) do
env[key] = value
end
return env
end

Expand Down
15 changes: 11 additions & 4 deletions lua/kulala/parser/init.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
local FS = require("kulala.utils.fs")
local GLOBALS = require("kulala.globals")
local GLOBAL_STORE = require("kulala.global_store")
local CONFIG = require("kulala.config")
local DB = require("kulala.db")
local DYNAMIC_VARS = require("kulala.parser.dynamic_vars")
local STRING_UTILS = require("kulala.utils.string")
local ENV_PARSER = require("kulala.parser.env")
local FS = require("kulala.utils.fs")
local GLOBALS = require("kulala.globals")
local GLOBAL_STORE = require("kulala.global_store")
local GRAPHQL_PARSER = require("kulala.parser.graphql")
local REQUEST_VARIABLES = require("kulala.parser.request_variables")
local STRING_UTILS = require("kulala.utils.string")
local PLUGIN_TMP_DIR = FS.get_plugin_tmp_dir()
local M = {}

Expand All @@ -32,6 +34,8 @@ local function parse_string_variables(str, variables)
value = variables[variable_name]
elseif env[variable_name] then
value = env[variable_name]
elseif REQUEST_VARIABLES.parse(variable_name) then
value = REQUEST_VARIABLES.parse(variable_name)
else
value = "{{" .. variable_name .. "}}"
vim.notify(
Expand Down Expand Up @@ -282,6 +286,8 @@ function M.parse()
local document_variables, requests = M.get_document()
local req = M.get_request_at_cursor(requests)

DB.data.previous_request = DB.data.current_request

document_variables = extend_document_variables(document_variables, req)

res.url = parse_url(req.url, document_variables)
Expand Down Expand Up @@ -394,6 +400,7 @@ function M.parse()
if CONFIG.get().debug then
FS.write_file(PLUGIN_TMP_DIR .. "/request.txt", table.concat(res.cmd, " "))
end
DB.data.current_request = res
return res
end

Expand Down
Loading

0 comments on commit 6f7c5dc

Please sign in to comment.