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

Add a javascript runner for running javascript before and after requests #516

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
doc/tags
.repro/
repro*.lua
lua/rest-nvim/script/node_modules
lua/rest-nvim/script/last_javascript.mjs
159 changes: 159 additions & 0 deletions lua/rest-nvim/script/javascript.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
local script = {}
local logger = require("rest-nvim.logger")

local has_js = vim.fn.executable('node')

if has_js == 0 then
print("node is not available")
end

local function cwd()
local current_script_path = debug.getinfo(1).source:match("@(.*)")
return current_script_path:match("(.*[/\\])") or "."
end

local function read_file(filename)
local file_to_open = cwd() .. "/" .. filename

local file = io.open(file_to_open, "r")
if file then
local file_content = file:read("*all")
file:close()
return file_content
else
print("Error: Could not open file " .. file_to_open)
end
end

local function write_file(filename, content)
local file_to_open = cwd() .. "/" .. filename
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if script file is saved for temporary use, it should be saved in temporary directory like $XDG_RUNTIME_DIR where you can find with stdpath("run")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure the best way to do this and still let node resolve dependencies

local file = io.open(file_to_open, "w")
if file then
file:write(content)
file:close()
else
print("Error: Could not open file " .. file_to_open)
end
return file_to_open
end

local function local_vars (ctx)
local ctx_vars = {}
for k, v in pairs(ctx.vars) do
ctx_vars[k] = v
end
for k, v in pairs(ctx.lv) do
ctx_vars[k] = v
end
return ctx_vars
end

local function create_prescript_env(ctx)
return {
_env = { cwd = cwd() },
request = { variables = local_vars(ctx) }
}
end

local function create_handler_env(ctx, res)
local response = res
-- TODO check mime type before parsing
local ok, decoded_body = pcall(vim.fn.json_decode, res.body)
if ok then
response = vim.deepcopy(res)
response.body = decoded_body
end

return {
_env = { cwd = cwd() },
client = { global = { data = {} } },
request = { variables = local_vars(ctx) },
response = response,
}
end

local function execute_cmd(cmd)
local handle = io.popen(cmd)
if handle then
local result = handle:read("*a")
handle:close()
return result
end
end

local js_str = read_file("javascript.mjs");

local function load_js(s, env)
local env_json = vim.fn.json_encode(env):gsub("\\", "\\\\") -- Escape backslashes

-- uncomment to load each time when developing
-- local js_str = read_file("javascript.mjs");

local js_code = string.format(js_str, env_json, s)

-- save to file so no need to escape quotes
local file_path = write_file('last_javascript.mjs', js_code)

local ok, result = pcall(function()
return execute_cmd("node " .. file_path)
end)
if not ok then
logger.error("JS execution error: " .. tostring(result))
return nil
end

return result
end

local function split_string_on_separator(multiline_str)
local before_separator = {}
local after_separator = {}
local found_separator = false

for line in multiline_str:gmatch("[^\r\n]+") do
if line == "-ENV-" then
found_separator = true
elseif found_separator then
table.insert(after_separator, line)
else
table.insert(before_separator, line)
end
end

return table.concat(before_separator), table.concat(after_separator)
end

local function update_local(ctx, env_json)
for key, value in pairs(env_json.request.variables) do
ctx:set_local(key, value)
end
end

local function update_global(env, env_json)
for key, value in pairs(env_json.client.global.data) do
env[key] = value
end
end

function script.load_pre_req_hook(s, ctx)
return function ()
local result = load_js(s, create_prescript_env(ctx))
local logs, json_str = split_string_on_separator(result)
print(logs)
local env_json = vim.fn.json_decode(json_str)
update_local(ctx, env_json)
end
end

function script.load_post_req_hook(s, ctx)
return function(res)
local result = load_js(s, create_handler_env(ctx, res))
local logs, json_str = split_string_on_separator(result)
print(logs)
local env_json = vim.fn.json_decode(json_str)
update_global(vim.env, env_json)
update_local(ctx, env_json)
end
end

return script
72 changes: 72 additions & 0 deletions lua/rest-nvim/script/javascript.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
try {
const ctx = JSON.parse(`%s`);

let jsonPath;
try {
let jsonPathModule = await import("jsonpath");
jsonPath = function (...args) {
return jsonPathModule.default.value(...args);
};
} catch (e) {
console.log(`jsonpath not found please install \`npm install --prefix ${ctx._env.cwd}\``);
}

let client;
if (ctx.client) {
// empty table json encoded as array by lua
ctx.client.global.data = Array.isArray(ctx.client.global.data) ? {} : ctx.client.global.data;

client = {
global: {
data: ctx.client.global.data,
get: function (key) {
return ctx.client.global.data[key];
},
set: function (key, value) {
ctx.client.global.data[key] = value;
},
},
};
}

let response;
if (ctx.response) {
//https://www.jetbrains.com/help/idea/http-response-reference.html
response = {
body: ctx.response.body,
headers: {
valueOf: function(key) {
return ctx.response.headers[key]?.[0]
},
valuesOf: function(key) {
return ctx.response.headers[key]
}
},
status: ctx.response.status.code,
contentType: {
mineType: ctx.response.headers["content-type"]?.[0]?.split(";")?.[0],
charset: ctx.response.headers["content-type"]?.[0].split(";")?.[0]
}
}
}

const request = {
variables: {
get: function (key) {
return ctx.request.variables[key];
},
set: function (key, value) {
ctx.request.variables[key] = value;
},
}
}

;(function(){ %s })();

console.log("-ENV-");
console.log(JSON.stringify(ctx));
} catch (error) {
console.log(error);
console.log("-ENV-");
console.log("{}");
}
Loading
Loading