Skip to content
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

feat(parser): named-requests + request-variables + replay() #93

Merged
merged 1 commit into from
Aug 5, 2024
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
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