Skip to content

Commit

Permalink
feat(scripts): add request.body.getComputed() (#262)
Browse files Browse the repository at this point in the history
Returns the `string` request body as sent via curl; with variables substituted,
or `undefined` if there is no body.

Very useful if you want to see the request body as it was sent to the server.

The `tryGetSubstituted` method will substitute variables with their values,
but leave the rest of the body as is.

If you have a GraphQL query in the body, for example, the `getComputed`
method will show the query as it was sent to the server,
which is quite different from the substituted version.

As an example, if you have a request body like this:

```graphql
query getRestClient($name: String!) {
  restclient(name: $name) {
    id
    name
    editorsSupported {
      name
    }
  }
}

{
  "variables": {
    "name": "{{ENV_VAR_CLIENT_NAME}}"
  }
}
```

Then the `getComputed` method will
return the body as it was sent to the server:

```json
{"query": "query getRestClient($name: String!) { restclient(name: $name) { id name editorsSupported { name } } } ", "variables": {"variables": {"name": "kulala"}}}
```

whereas the `tryGetSubstituted` method will
return the body with variables substituted as seen in your script:

```graphql
query getRestClient($name: String!) {
  restclient(name: $name) {
    id
    name
    editorsSupported {
      name
    }
  }
}

{
  "variables": {
    "name": "kulala"
  }
}
```

The `getComputed` method is always `undefined` for binary bodies.
  • Loading branch information
gorillamoe authored Oct 6, 2024
1 parent 757f87c commit 3407b68
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 21 deletions.
78 changes: 76 additions & 2 deletions docs/docs/scripts/request-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ client.log(request.variables.get("SOME_TOKEN"));
## request.body.getRaw
Returns the request body in the raw format:
Returns the request body `string` in
the raw format (or `undefined` if there is no body).
If the body contains variables,
their names are displayed instead of their values.
Expand All @@ -37,12 +38,85 @@ client.log(request.body.getRaw());
## request.body.tryGetSubstituted
Returns the request body with variables substituted.
Returns the request body with variables substituted
(or `undefined` if there is no body).
```javascript
client.log(request.body.tryGetSubstituted());
```
## request.body.getComputed
Returns the `string` request body as sent via curl; with variables substituted,
or `undefined` if there is no body.
:::tip
Useful if you want to see the request body as it was sent to the server.
The `tryGetSubstituted` method will substitute variables with their values,
but leave the rest of the body as is.
If you have a GraphQL query in the body, for example, the `getComputed`
method will show the query as it was sent to the server,
which is quite different from the substituted version.
:::
As an example, if you have a request body like this:
```graphql
query getRestClient($name: String!) {
restclient(name: $name) {
id
name
editorsSupported {
name
}
}
}
{
"variables": {
"name": "{{ENV_VAR_CLIENT_NAME}}"
}
}
```
Then the `getComputed` method will
return the body as it was sent to the server:
```json
{"query": "query getRestClient($name: String!) { restclient(name: $name) { id name editorsSupported { name } } } ", "variables": {"variables": {"name": "kulala"}}}
```
whereas the `tryGetSubstituted` method will
return the body with variables substituted as seen in your script:
```graphql
query getRestClient($name: String!) {
restclient(name: $name) {
id
name
editorsSupported {
name
}
}
}
{
"variables": {
"name": "kulala"
}
}
```
:::warning
The `getComputed` method is always `undefined` for binary bodies.
:::
## request.environment.get
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 @@ -4,7 +4,7 @@ local M = {}

local plugin_tmp_dir = FS.get_plugin_tmp_dir()

M.VERSION = "4.0.4"
M.VERSION = "4.1.0"
M.UI_ID = "kulala://ui"
M.SCRATCHPAD_ID = "kulala://scratchpad"
M.HEADERS_FILE = plugin_tmp_dir .. "/headers.txt"
Expand Down
29 changes: 21 additions & 8 deletions lua/kulala/parser/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ end
---@field headers table -- The headers with variables and dynamic variables replaced
---@field headers_raw table -- The headers as they appear in the document
---@field body_raw string|nil -- The raw body as it appears in the document
---@field body_computed string|nil -- The computed body as sent by curl; with variables and dynamic variables replaced
---@field body string|nil -- The body with variables and dynamic variables replaced
---@field environment table -- The environment- and document-variables
---@field cmd table -- The command to execute the request
Expand All @@ -488,6 +489,7 @@ function M.get_basic_request_data(start_request_linenr)
headers_raw = {},
body = nil,
body_raw = nil,
body_computed = nil,
cmd = {},
ft = "text",
environment = {},
Expand Down Expand Up @@ -583,6 +585,23 @@ M.parse = function(start_request_linenr)
end
end

local is_graphql = PARSER_UTILS.contains_meta_tag(res, "graphql")
or PARSER_UTILS.contains_header(res.headers, "x-request-type", "graphql")
if res.body ~= nil then
if is_graphql then
local gql_json = GRAPHQL_PARSER.get_json(res.body)
if gql_json then
res.body_computed = gql_json
end
else
res.body_computed = res.body
end
end
if CONFIG.get().treesitter then
-- treesitter parser handles graphql requests before this point
is_graphql = false
end

FS.write_file(GLOBALS.REQUEST_FILE, vim.fn.json_encode(res), false)
-- PERF: We only want to run the scripts if they exist
-- Also we don't want to re-run the environment replace_variables_in_url_headers_body
Expand Down Expand Up @@ -611,13 +630,6 @@ M.parse = function(start_request_linenr)
table.insert(res.cmd, "-X")
table.insert(res.cmd, res.method)

local is_graphql = PARSER_UTILS.contains_meta_tag(res, "graphql")
or PARSER_UTILS.contains_header(res.headers, "x-request-type", "graphql")
if CONFIG.get().treesitter then
-- treesitter parser handles graphql requests before this point
is_graphql = false
end

local content_type_header_name, content_type_header_value = PARSER_UTILS.get_header(res.headers, "content-type")

if content_type_header_name and content_type_header_value and res.body ~= nil then
Expand Down Expand Up @@ -651,7 +663,8 @@ M.parse = function(start_request_linenr)
if gql_json then
table.insert(res.cmd, "--data")
table.insert(res.cmd, gql_json)
res.headers[content_type_header_name] = "application/json"
res.headers["content-type"] = "application/json"
res.body_computed = gql_json
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface RequestJson {
headers: Record<string, string>,
headers_raw: Record<string, string>,
body_raw: string,
body_computed: string | undefined,
body: string | object,
method: string,
url_raw: string,
Expand Down Expand Up @@ -59,6 +60,9 @@ export const Request = {
tryGetSubstituted: () => {
return req.body;
},
getComputed: () => {
return req.body_computed;
},
},
headers: {
findByName: (headerName: string) => {
Expand Down
29 changes: 19 additions & 10 deletions lua/kulala/utils/fs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,10 @@ end

---Delete all files in a directory
---@param dir string
---@param verbose boolean|nil
---@usage fs.delete_files_in_directory('tmp', true)
M.delete_files_in_directory = function(dir, verbose)
---@usage fs.delete_files_in_directory('tmp')
---@return string[] deleted_files
M.delete_files_in_directory = function(dir)
local deleted_files = {}
-- Open the directory for scanning
local scandir = vim.loop.fs_scandir(dir)
if scandir then
Expand All @@ -219,15 +220,15 @@ M.delete_files_in_directory = function(dir, verbose)
local success, err = vim.loop.fs_unlink(filepath)
if not success then
print("Error deleting file:", filepath, err)
end
if verbose and success then
Logger.info("Deleted file: " .. filepath)
else
table.insert(deleted_files, filepath)
end
end
end
else
print("Error opening directory:", dir)
end
return deleted_files
end

M.delete_request_scripts_files = function()
Expand Down Expand Up @@ -330,10 +331,18 @@ M.clear_cached_files = function()
local scripts_dir = M.get_tmp_scripts_dir()
local request_scripts_dir = M.get_request_scripts_dir()
local compiled_pre_request_scripts = M.join_paths(M.get_scripts_dir(), "engines", "javascript", "lib", "dist")
M.delete_files_in_directory(tmp_dir, true)
M.delete_files_in_directory(scripts_dir, true)
M.delete_files_in_directory(request_scripts_dir, true)
M.delete_files_in_directory(compiled_pre_request_scripts, true)
local deleted_files = {}
deleted_files = vim.tbl_extend("force", deleted_files, M.delete_files_in_directory(tmp_dir))
deleted_files = vim.tbl_extend("force", deleted_files, M.delete_files_in_directory(scripts_dir))
deleted_files = vim.tbl_extend("force", deleted_files, M.delete_files_in_directory(request_scripts_dir))
deleted_files = vim.tbl_extend("force", deleted_files, M.delete_files_in_directory(compiled_pre_request_scripts))
local string_list = vim.fn.join(
vim.tbl_map(function(file)
return "- " .. file
end, deleted_files),
"\n"
)
Logger.info("Deleted files:\n" .. string_list)
end

return M

0 comments on commit 3407b68

Please sign in to comment.