feat(plugins): web requests api (#2879)
* feat(plugins): web requests api * fix e2e tests * fix e2e tests again
This commit is contained in:
parent
41e953f177
commit
b59b29a534
22 changed files with 1124 additions and 108 deletions
694
Cargo.lock
generated
694
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -48,6 +48,7 @@ impl ZellijPlugin for State {
|
|||
PermissionType::RunCommands,
|
||||
PermissionType::OpenTerminalsOrPlugins,
|
||||
PermissionType::WriteToStdin,
|
||||
PermissionType::WebAccess,
|
||||
]);
|
||||
self.configuration = configuration;
|
||||
subscribe(&[
|
||||
|
|
@ -257,6 +258,22 @@ impl ZellijPlugin for State {
|
|||
context,
|
||||
);
|
||||
},
|
||||
Key::Ctrl('4') => {
|
||||
let mut headers = BTreeMap::new();
|
||||
let mut context = BTreeMap::new();
|
||||
let body = vec![1, 2, 3];
|
||||
headers.insert("header1".to_owned(), "value1".to_owned());
|
||||
headers.insert("header2".to_owned(), "value2".to_owned());
|
||||
context.insert("user_key_1".to_owned(), "user_value1".to_owned());
|
||||
context.insert("user_key_2".to_owned(), "user_value2".to_owned());
|
||||
web_request(
|
||||
"https://example.com/foo?arg1=val1&arg2=val2",
|
||||
HttpVerb::Post,
|
||||
headers,
|
||||
body,
|
||||
context,
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
Event::CustomMessage(message, payload) => {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,13 @@ use zellij_utils::consts::{
|
|||
session_info_cache_file_name, session_info_folder_for_session, session_layout_cache_file_name,
|
||||
ZELLIJ_SOCK_DIR,
|
||||
};
|
||||
use zellij_utils::data::{Event, SessionInfo};
|
||||
use zellij_utils::data::{Event, HttpVerb, SessionInfo};
|
||||
use zellij_utils::errors::{prelude::*, BackgroundJobContext, ContextType};
|
||||
use zellij_utils::surf::{
|
||||
self,
|
||||
http::{Method, Url},
|
||||
RequestBuilder,
|
||||
};
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fs;
|
||||
|
|
@ -40,6 +45,15 @@ pub enum BackgroundJob {
|
|||
PathBuf,
|
||||
BTreeMap<String, String>,
|
||||
), // command, args, env_variables, cwd, context
|
||||
WebRequest(
|
||||
PluginId,
|
||||
ClientId,
|
||||
String, // url
|
||||
HttpVerb,
|
||||
BTreeMap<String, String>, // headers
|
||||
Vec<u8>, // body
|
||||
BTreeMap<String, String>, // context
|
||||
),
|
||||
Exit,
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +71,7 @@ impl From<&BackgroundJob> for BackgroundJobContext {
|
|||
BackgroundJob::ReportSessionInfo(..) => BackgroundJobContext::ReportSessionInfo,
|
||||
BackgroundJob::ReportLayoutInfo(..) => BackgroundJobContext::ReportLayoutInfo,
|
||||
BackgroundJob::RunCommand(..) => BackgroundJobContext::RunCommand,
|
||||
BackgroundJob::WebRequest(..) => BackgroundJobContext::WebRequest,
|
||||
BackgroundJob::Exit => BackgroundJobContext::Exit,
|
||||
}
|
||||
}
|
||||
|
|
@ -285,6 +300,69 @@ pub(crate) fn background_jobs_main(bus: Bus<BackgroundJob>) -> Result<()> {
|
|||
}
|
||||
});
|
||||
},
|
||||
BackgroundJob::WebRequest(plugin_id, client_id, url, verb, headers, body, context) => {
|
||||
task::spawn({
|
||||
let senders = bus.senders.clone();
|
||||
async move {
|
||||
async fn web_request(
|
||||
url: String,
|
||||
verb: HttpVerb,
|
||||
headers: BTreeMap<String, String>,
|
||||
body: Vec<u8>,
|
||||
) -> Result<
|
||||
(u16, BTreeMap<String, String>, Vec<u8>), // status_code, headers, body
|
||||
zellij_utils::surf::Error,
|
||||
> {
|
||||
let url = Url::parse(&url)?;
|
||||
let http_method = match verb {
|
||||
HttpVerb::Get => Method::Get,
|
||||
HttpVerb::Post => Method::Post,
|
||||
HttpVerb::Put => Method::Put,
|
||||
HttpVerb::Delete => Method::Delete,
|
||||
};
|
||||
let mut req = RequestBuilder::new(http_method, url);
|
||||
if !body.is_empty() {
|
||||
req = req.body(body);
|
||||
}
|
||||
for (header, value) in headers {
|
||||
req = req.header(header.as_str(), value);
|
||||
}
|
||||
let mut res = req.await?;
|
||||
let status_code = res.status();
|
||||
let headers: BTreeMap<String, String> = res
|
||||
.iter()
|
||||
.map(|(name, value)| (name.to_string(), value.to_string()))
|
||||
.collect();
|
||||
let body = res.take_body().into_bytes().await?;
|
||||
Ok((status_code as u16, headers, body))
|
||||
}
|
||||
|
||||
match web_request(url, verb, headers, body).await {
|
||||
Ok((status, headers, body)) => {
|
||||
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
|
||||
Some(plugin_id),
|
||||
Some(client_id),
|
||||
Event::WebRequestResult(status, headers, body, context),
|
||||
)]));
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Failed to send web request: {}", e);
|
||||
let error_body = e.to_string().as_bytes().to_vec();
|
||||
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
|
||||
Some(plugin_id),
|
||||
Some(client_id),
|
||||
Event::WebRequestResult(
|
||||
400,
|
||||
BTreeMap::new(),
|
||||
error_body,
|
||||
context,
|
||||
),
|
||||
)]));
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
BackgroundJob::Exit => {
|
||||
for loading_plugin in loading_plugins.values() {
|
||||
loading_plugin.store(false, Ordering::SeqCst);
|
||||
|
|
|
|||
|
|
@ -546,6 +546,7 @@ fn create_plugin_thread_with_background_jobs_receiver(
|
|||
let _ = to_screen.send(ScreenInstruction::Exit);
|
||||
let _ = to_server.send(ServerInstruction::KillSession);
|
||||
let _ = to_plugin.send(PluginInstruction::Exit);
|
||||
let _ = to_background_jobs.send(BackgroundJob::Exit);
|
||||
let _ = plugin_thread.join();
|
||||
}
|
||||
};
|
||||
|
|
@ -5418,3 +5419,78 @@ pub fn run_command_with_env_vars_and_cwd_plugin_command() {
|
|||
.clone();
|
||||
assert_snapshot!(format!("{:#?}", new_tab_event));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
pub fn web_request_plugin_command() {
|
||||
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
|
||||
// destructor removes the directory
|
||||
let plugin_host_folder = PathBuf::from(temp_folder.path());
|
||||
let cache_path = plugin_host_folder.join("permissions_test.kdl");
|
||||
let (plugin_thread_sender, background_jobs_receiver, screen_receiver, teardown) =
|
||||
create_plugin_thread_with_background_jobs_receiver(Some(plugin_host_folder));
|
||||
let plugin_should_float = Some(false);
|
||||
let plugin_title = Some("test_plugin".to_owned());
|
||||
let run_plugin = RunPlugin {
|
||||
_allow_exec_host_cmd: false,
|
||||
location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
|
||||
configuration: Default::default(),
|
||||
};
|
||||
let tab_index = 1;
|
||||
let client_id = 1;
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let received_background_jobs_instructions = Arc::new(Mutex::new(vec![]));
|
||||
let background_jobs_thread = log_actions_in_thread!(
|
||||
received_background_jobs_instructions,
|
||||
BackgroundJob::WebRequest,
|
||||
background_jobs_receiver,
|
||||
1
|
||||
);
|
||||
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
|
||||
let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
|
||||
received_screen_instructions,
|
||||
ScreenInstruction::Exit,
|
||||
screen_receiver,
|
||||
1,
|
||||
&PermissionType::WebAccess,
|
||||
cache_path,
|
||||
plugin_thread_sender,
|
||||
client_id
|
||||
);
|
||||
|
||||
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
|
||||
let _ = plugin_thread_sender.send(PluginInstruction::Load(
|
||||
plugin_should_float,
|
||||
false,
|
||||
plugin_title,
|
||||
run_plugin,
|
||||
tab_index,
|
||||
None,
|
||||
client_id,
|
||||
size,
|
||||
));
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||
None,
|
||||
Some(client_id),
|
||||
Event::Key(Key::Ctrl('4')), // this triggers the enent in the fixture plugin
|
||||
)]));
|
||||
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||
teardown();
|
||||
let new_tab_event = received_background_jobs_instructions
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find_map(|i| {
|
||||
if let BackgroundJob::WebRequest(..) = i {
|
||||
Some(i.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.clone();
|
||||
assert_snapshot!(format!("{:#?}", new_tab_event));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4864
|
||||
assertion_line: 5189
|
||||
expression: "format!(\"{:#?}\", permissions)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -11,5 +11,6 @@ Some(
|
|||
RunCommands,
|
||||
OpenTerminalsOrPlugins,
|
||||
WriteToStdin,
|
||||
WebAccess,
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 4767
|
||||
assertion_line: 5101
|
||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -13,5 +13,6 @@ Some(
|
|||
RunCommands,
|
||||
OpenTerminalsOrPlugins,
|
||||
WriteToStdin,
|
||||
WebAccess,
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||
assertion_line: 5494
|
||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||
---
|
||||
Some(
|
||||
WebRequest(
|
||||
0,
|
||||
1,
|
||||
"https://example.com/foo?arg1=val1&arg2=val2",
|
||||
Post,
|
||||
{
|
||||
"header1": "value1",
|
||||
"header2": "value2",
|
||||
},
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
],
|
||||
{
|
||||
"user_key_1": "user_value1",
|
||||
"user_key_2": "user_value2",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
|
@ -18,7 +18,7 @@ use std::{
|
|||
use wasmer::{imports, AsStoreMut, Function, FunctionEnv, FunctionEnvMut, Imports};
|
||||
use wasmer_wasi::WasiEnv;
|
||||
use zellij_utils::data::{
|
||||
CommandType, ConnectToSession, PermissionStatus, PermissionType, PluginPermission,
|
||||
CommandType, ConnectToSession, HttpVerb, PermissionStatus, PermissionType, PluginPermission,
|
||||
};
|
||||
use zellij_utils::input::permission::PermissionCache;
|
||||
|
||||
|
|
@ -130,6 +130,9 @@ fn host_run_plugin_command(env: FunctionEnvMut<ForeignFunctionEnv>) {
|
|||
PluginCommand::RunCommand(command_line, env_variables, cwd, context) => {
|
||||
run_command(env, command_line, env_variables, cwd, context)
|
||||
},
|
||||
PluginCommand::WebRequest(url, verb, headers, body, context) => {
|
||||
web_request(env, url, verb, headers, body, context)
|
||||
},
|
||||
PluginCommand::PostMessageTo(plugin_message) => {
|
||||
post_message_to(env, plugin_message)?
|
||||
},
|
||||
|
|
@ -607,12 +610,6 @@ fn run_command(
|
|||
cwd: PathBuf,
|
||||
context: BTreeMap<String, String>,
|
||||
) {
|
||||
let err_context = || {
|
||||
format!(
|
||||
"failed to execute command on host for plugin '{}'",
|
||||
env.plugin_env.name()
|
||||
)
|
||||
};
|
||||
if command_line.is_empty() {
|
||||
log::error!("Command cannot be empty");
|
||||
} else {
|
||||
|
|
@ -632,6 +629,28 @@ fn run_command(
|
|||
}
|
||||
}
|
||||
|
||||
fn web_request(
|
||||
env: &ForeignFunctionEnv,
|
||||
url: String,
|
||||
verb: HttpVerb,
|
||||
headers: BTreeMap<String, String>,
|
||||
body: Vec<u8>,
|
||||
context: BTreeMap<String, String>,
|
||||
) {
|
||||
let _ = env
|
||||
.plugin_env
|
||||
.senders
|
||||
.send_to_background_jobs(BackgroundJob::WebRequest(
|
||||
env.plugin_env.plugin_id,
|
||||
env.plugin_env.client_id,
|
||||
url,
|
||||
verb,
|
||||
headers,
|
||||
body,
|
||||
context,
|
||||
));
|
||||
}
|
||||
|
||||
fn post_message_to(env: &ForeignFunctionEnv, plugin_message: PluginMessage) -> Result<()> {
|
||||
let worker_name = plugin_message
|
||||
.worker_name
|
||||
|
|
@ -1198,6 +1217,7 @@ fn check_command_permission(
|
|||
| PluginCommand::OpenCommandPaneInPlace(..)
|
||||
| PluginCommand::RunCommand(..)
|
||||
| PluginCommand::ExecCmd(..) => PermissionType::RunCommands,
|
||||
PluginCommand::WebRequest(..) => PermissionType::WebAccess,
|
||||
PluginCommand::Write(..) | PluginCommand::WriteChars(..) => PermissionType::WriteToStdin,
|
||||
PluginCommand::SwitchTabTo(..)
|
||||
| PluginCommand::SwitchToMode(..)
|
||||
|
|
|
|||
|
|
@ -207,6 +207,21 @@ pub fn run_command_with_env_variables_and_cwd(
|
|||
unsafe { host_run_plugin_command() };
|
||||
}
|
||||
|
||||
pub fn web_request<S: AsRef<str>>(
|
||||
url: S,
|
||||
verb: HttpVerb,
|
||||
headers: BTreeMap<String, String>,
|
||||
body: Vec<u8>,
|
||||
context: BTreeMap<String, String>,
|
||||
) where
|
||||
S: ToString,
|
||||
{
|
||||
let plugin_command = PluginCommand::WebRequest(url.to_string(), verb, headers, body, context);
|
||||
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
|
||||
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
|
||||
unsafe { host_run_plugin_command() };
|
||||
}
|
||||
|
||||
/// Hide the plugin pane (suppress it) from the UI
|
||||
pub fn hide_self() {
|
||||
let plugin_command = PluginCommand::HideSelf;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ interprocess = "1.2.1"
|
|||
async-std = { version = "1.3.0", features = ["unstable"] }
|
||||
notify-debouncer-full = "0.1.0"
|
||||
humantime = "2.1.0"
|
||||
surf = { version = "2.3.2", default-features = false, features = ["h1-client-rustls"] }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.6.0", features = ["backtrace"] }
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub struct Event {
|
|||
pub name: i32,
|
||||
#[prost(
|
||||
oneof = "event::Payload",
|
||||
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14"
|
||||
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15"
|
||||
)]
|
||||
pub payload: ::core::option::Option<event::Payload>,
|
||||
}
|
||||
|
|
@ -46,6 +46,8 @@ pub mod event {
|
|||
SessionUpdatePayload(super::SessionUpdatePayload),
|
||||
#[prost(message, tag = "14")]
|
||||
RunCommandResultPayload(super::RunCommandResultPayload),
|
||||
#[prost(message, tag = "15")]
|
||||
WebRequestResultPayload(super::WebRequestResultPayload),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
|
|
@ -68,6 +70,18 @@ pub struct RunCommandResultPayload {
|
|||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct WebRequestResultPayload {
|
||||
#[prost(int32, tag = "1")]
|
||||
pub status: i32,
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub headers: ::prost::alloc::vec::Vec<Header>,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub body: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(message, repeated, tag = "4")]
|
||||
pub context: ::prost::alloc::vec::Vec<ContextItem>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ContextItem {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
|
|
@ -76,6 +90,14 @@ pub struct ContextItem {
|
|||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Header {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "2")]
|
||||
pub value: ::prost::alloc::string::String,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PermissionRequestResultPayload {
|
||||
#[prost(bool, tag = "1")]
|
||||
pub granted: bool,
|
||||
|
|
@ -287,6 +309,7 @@ pub enum EventType {
|
|||
PermissionRequestResult = 15,
|
||||
SessionUpdate = 16,
|
||||
RunCommandResult = 17,
|
||||
WebRequestResult = 18,
|
||||
}
|
||||
impl EventType {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
|
|
@ -313,6 +336,7 @@ impl EventType {
|
|||
EventType::PermissionRequestResult => "PermissionRequestResult",
|
||||
EventType::SessionUpdate => "SessionUpdate",
|
||||
EventType::RunCommandResult => "RunCommandResult",
|
||||
EventType::WebRequestResult => "WebRequestResult",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
|
|
@ -336,6 +360,7 @@ impl EventType {
|
|||
"PermissionRequestResult" => Some(Self::PermissionRequestResult),
|
||||
"SessionUpdate" => Some(Self::SessionUpdate),
|
||||
"RunCommandResult" => Some(Self::RunCommandResult),
|
||||
"WebRequestResult" => Some(Self::WebRequestResult),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ pub struct PluginCommand {
|
|||
pub name: i32,
|
||||
#[prost(
|
||||
oneof = "plugin_command::Payload",
|
||||
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43"
|
||||
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44"
|
||||
)]
|
||||
pub payload: ::core::option::Option<plugin_command::Payload>,
|
||||
}
|
||||
|
|
@ -98,6 +98,8 @@ pub mod plugin_command {
|
|||
OpenCommandPaneInPlacePayload(super::OpenCommandPanePayload),
|
||||
#[prost(message, tag = "43")]
|
||||
RunCommandPayload(super::RunCommandPayload),
|
||||
#[prost(message, tag = "44")]
|
||||
WebRequestPayload(super::WebRequestPayload),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
|
|
@ -178,6 +180,20 @@ pub struct RunCommandPayload {
|
|||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct WebRequestPayload {
|
||||
#[prost(string, tag = "1")]
|
||||
pub url: ::prost::alloc::string::String,
|
||||
#[prost(enumeration = "HttpVerb", tag = "2")]
|
||||
pub verb: i32,
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub headers: ::prost::alloc::vec::Vec<super::event::Header>,
|
||||
#[prost(bytes = "vec", tag = "4")]
|
||||
pub body: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub context: ::prost::alloc::vec::Vec<ContextItem>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct EnvVariable {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
|
|
@ -294,6 +310,7 @@ pub enum CommandName {
|
|||
OpenCommandInPlace = 69,
|
||||
OpenFileInPlace = 70,
|
||||
RunCommand = 71,
|
||||
WebRequest = 72,
|
||||
}
|
||||
impl CommandName {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
|
|
@ -374,6 +391,7 @@ impl CommandName {
|
|||
CommandName::OpenCommandInPlace => "OpenCommandInPlace",
|
||||
CommandName::OpenFileInPlace => "OpenFileInPlace",
|
||||
CommandName::RunCommand => "RunCommand",
|
||||
CommandName::WebRequest => "WebRequest",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
|
|
@ -451,6 +469,39 @@ impl CommandName {
|
|||
"OpenCommandInPlace" => Some(Self::OpenCommandInPlace),
|
||||
"OpenFileInPlace" => Some(Self::OpenFileInPlace),
|
||||
"RunCommand" => Some(Self::RunCommand),
|
||||
"WebRequest" => Some(Self::WebRequest),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum HttpVerb {
|
||||
Get = 0,
|
||||
Post = 1,
|
||||
Put = 2,
|
||||
Delete = 3,
|
||||
}
|
||||
impl HttpVerb {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
HttpVerb::Get => "Get",
|
||||
HttpVerb::Post => "Post",
|
||||
HttpVerb::Put => "Put",
|
||||
HttpVerb::Delete => "Delete",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"Get" => Some(Self::Get),
|
||||
"Post" => Some(Self::Post),
|
||||
"Put" => Some(Self::Put),
|
||||
"Delete" => Some(Self::Delete),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ pub enum PermissionType {
|
|||
RunCommands = 3,
|
||||
OpenTerminalsOrPlugins = 4,
|
||||
WriteToStdin = 5,
|
||||
WebAccess = 6,
|
||||
}
|
||||
impl PermissionType {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
|
|
@ -21,6 +22,7 @@ impl PermissionType {
|
|||
PermissionType::RunCommands => "RunCommands",
|
||||
PermissionType::OpenTerminalsOrPlugins => "OpenTerminalsOrPlugins",
|
||||
PermissionType::WriteToStdin => "WriteToStdin",
|
||||
PermissionType::WebAccess => "WebAccess",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
|
|
@ -32,6 +34,7 @@ impl PermissionType {
|
|||
"RunCommands" => Some(Self::RunCommands),
|
||||
"OpenTerminalsOrPlugins" => Some(Self::OpenTerminalsOrPlugins),
|
||||
"WriteToStdin" => Some(Self::WriteToStdin),
|
||||
"WebAccess" => Some(Self::WebAccess),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -497,7 +497,16 @@ pub enum Event {
|
|||
PermissionRequestResult(PermissionStatus),
|
||||
SessionUpdate(Vec<SessionInfo>),
|
||||
RunCommandResult(Option<i32>, Vec<u8>, Vec<u8>, BTreeMap<String, String>), // exit_code, STDOUT, STDERR,
|
||||
// context
|
||||
// context
|
||||
WebRequestResult(
|
||||
u16,
|
||||
BTreeMap<String, String>,
|
||||
Vec<u8>,
|
||||
BTreeMap<String, String>,
|
||||
), // status,
|
||||
// headers,
|
||||
// body,
|
||||
// context
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
|
@ -524,6 +533,7 @@ pub enum Permission {
|
|||
RunCommands,
|
||||
OpenTerminalsOrPlugins,
|
||||
WriteToStdin,
|
||||
WebAccess,
|
||||
}
|
||||
|
||||
impl PermissionType {
|
||||
|
|
@ -539,6 +549,7 @@ impl PermissionType {
|
|||
PermissionType::RunCommands => "Run commands".to_owned(),
|
||||
PermissionType::OpenTerminalsOrPlugins => "Start new terminals and plugins".to_owned(),
|
||||
PermissionType::WriteToStdin => "Write to standard input (STDIN)".to_owned(),
|
||||
PermissionType::WebAccess => "Make web requests".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -991,6 +1002,14 @@ impl PluginMessage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum HttpVerb {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, EnumDiscriminants, ToString)]
|
||||
#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
|
||||
#[strum_discriminants(name(CommandType))]
|
||||
|
|
@ -1067,12 +1086,16 @@ pub enum PluginCommand {
|
|||
OpenFileInPlace(FileToOpen),
|
||||
OpenCommandPaneInPlace(CommandToRun),
|
||||
RunCommand(
|
||||
Vec<String>,
|
||||
BTreeMap<String, String>,
|
||||
PathBuf,
|
||||
BTreeMap<String, String>,
|
||||
), // command,
|
||||
// env_Variables,
|
||||
// cwd,
|
||||
// context
|
||||
Vec<String>, // command
|
||||
BTreeMap<String, String>, // env_variables
|
||||
PathBuf, // cwd
|
||||
BTreeMap<String, String>, // context
|
||||
),
|
||||
WebRequest(
|
||||
String, // url
|
||||
HttpVerb,
|
||||
BTreeMap<String, String>, // headers
|
||||
Vec<u8>, // body
|
||||
BTreeMap<String, String>, // context
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -447,6 +447,7 @@ pub enum BackgroundJobContext {
|
|||
ReportSessionInfo,
|
||||
ReportLayoutInfo,
|
||||
RunCommand,
|
||||
WebRequest,
|
||||
Exit,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ pub mod logging; // Requires log4rs
|
|||
#[cfg(not(target_family = "wasm"))]
|
||||
pub use ::{
|
||||
anyhow, async_channel, async_std, clap, common_path, humantime, interprocess, lazy_static,
|
||||
libc, miette, nix, notify_debouncer_full, regex, serde, signal_hook, tempfile, termwiz, vte,
|
||||
libc, miette, nix, notify_debouncer_full, regex, serde, signal_hook, surf, tempfile, termwiz,
|
||||
vte,
|
||||
};
|
||||
|
||||
pub use ::prost;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ enum EventType {
|
|||
PermissionRequestResult = 15;
|
||||
SessionUpdate = 16;
|
||||
RunCommandResult = 17;
|
||||
WebRequestResult = 18;
|
||||
}
|
||||
|
||||
message EventNameList {
|
||||
|
|
@ -63,6 +64,7 @@ message Event {
|
|||
PermissionRequestResultPayload permission_request_result_payload = 12;
|
||||
SessionUpdatePayload session_update_payload = 13;
|
||||
RunCommandResultPayload run_command_result_payload = 14;
|
||||
WebRequestResultPayload web_request_result_payload = 15;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,11 +79,23 @@ message RunCommandResultPayload {
|
|||
repeated ContextItem context = 4;
|
||||
}
|
||||
|
||||
message WebRequestResultPayload {
|
||||
int32 status = 1;
|
||||
repeated Header headers = 2;
|
||||
bytes body = 3;
|
||||
repeated ContextItem context = 4;
|
||||
}
|
||||
|
||||
message ContextItem {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message Header {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message PermissionRequestResultPayload {
|
||||
bool granted = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,25 @@ impl TryFrom<ProtobufEvent> for Event {
|
|||
},
|
||||
_ => Err("Malformed payload for the RunCommandResult Event"),
|
||||
},
|
||||
Some(ProtobufEventType::WebRequestResult) => match protobuf_event.payload {
|
||||
Some(ProtobufEventPayload::WebRequestResultPayload(web_request_result_payload)) => {
|
||||
Ok(Event::WebRequestResult(
|
||||
web_request_result_payload.status as u16,
|
||||
web_request_result_payload
|
||||
.headers
|
||||
.into_iter()
|
||||
.map(|h| (h.name, h.value))
|
||||
.collect(),
|
||||
web_request_result_payload.body,
|
||||
web_request_result_payload
|
||||
.context
|
||||
.into_iter()
|
||||
.map(|c_i| (c_i.name, c_i.value))
|
||||
.collect(),
|
||||
))
|
||||
},
|
||||
_ => Err("Malformed payload for the WebRequestResult Event"),
|
||||
},
|
||||
None => Err("Unknown Protobuf Event"),
|
||||
}
|
||||
}
|
||||
|
|
@ -370,6 +389,26 @@ impl TryFrom<Event> for ProtobufEvent {
|
|||
)),
|
||||
})
|
||||
},
|
||||
Event::WebRequestResult(status, headers, body, context) => {
|
||||
let web_request_result_payload = WebRequestResultPayload {
|
||||
status: status as i32,
|
||||
headers: headers
|
||||
.into_iter()
|
||||
.map(|(name, value)| Header { name, value })
|
||||
.collect(),
|
||||
body,
|
||||
context: context
|
||||
.into_iter()
|
||||
.map(|(name, value)| ContextItem { name, value })
|
||||
.collect(),
|
||||
};
|
||||
Ok(ProtobufEvent {
|
||||
name: ProtobufEventType::WebRequestResult as i32,
|
||||
payload: Some(event::Payload::WebRequestResultPayload(
|
||||
web_request_result_payload,
|
||||
)),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -816,6 +855,7 @@ impl TryFrom<ProtobufEventType> for EventType {
|
|||
ProtobufEventType::PermissionRequestResult => EventType::PermissionRequestResult,
|
||||
ProtobufEventType::SessionUpdate => EventType::SessionUpdate,
|
||||
ProtobufEventType::RunCommandResult => EventType::RunCommandResult,
|
||||
ProtobufEventType::WebRequestResult => EventType::WebRequestResult,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -842,6 +882,7 @@ impl TryFrom<EventType> for ProtobufEventType {
|
|||
EventType::PermissionRequestResult => ProtobufEventType::PermissionRequestResult,
|
||||
EventType::SessionUpdate => ProtobufEventType::SessionUpdate,
|
||||
EventType::RunCommandResult => ProtobufEventType::RunCommandResult,
|
||||
EventType::WebRequestResult => ProtobufEventType::WebRequestResult,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ enum CommandName {
|
|||
OpenCommandInPlace = 69;
|
||||
OpenFileInPlace = 70;
|
||||
RunCommand = 71;
|
||||
WebRequest = 72;
|
||||
}
|
||||
|
||||
message PluginCommand {
|
||||
|
|
@ -130,6 +131,7 @@ message PluginCommand {
|
|||
OpenFilePayload open_terminal_in_place_payload = 41;
|
||||
OpenCommandPanePayload open_command_pane_in_place_payload = 42;
|
||||
RunCommandPayload run_command_payload = 43;
|
||||
WebRequestPayload web_request_payload = 44;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +181,21 @@ message RunCommandPayload {
|
|||
repeated ContextItem context = 4;
|
||||
}
|
||||
|
||||
message WebRequestPayload {
|
||||
string url = 1;
|
||||
HttpVerb verb = 2;
|
||||
repeated event.Header headers = 3;
|
||||
bytes body = 4;
|
||||
repeated ContextItem context = 5;
|
||||
}
|
||||
|
||||
enum HttpVerb {
|
||||
Get = 0;
|
||||
Post = 1;
|
||||
Put = 2;
|
||||
Delete = 3;
|
||||
}
|
||||
|
||||
message EnvVariable {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,47 @@
|
|||
pub use super::generated_api::api::{
|
||||
action::{PaneIdAndShouldFloat, SwitchToModePayload},
|
||||
event::EventNameList as ProtobufEventNameList,
|
||||
event::{EventNameList as ProtobufEventNameList, Header},
|
||||
input_mode::InputMode as ProtobufInputMode,
|
||||
plugin_command::{
|
||||
plugin_command::Payload, CommandName, ContextItem, EnvVariable, ExecCmdPayload,
|
||||
IdAndNewName, MovePayload, OpenCommandPanePayload, OpenFilePayload,
|
||||
PluginCommand as ProtobufPluginCommand, PluginMessagePayload,
|
||||
HttpVerb as ProtobufHttpVerb, IdAndNewName, MovePayload, OpenCommandPanePayload,
|
||||
OpenFilePayload, PluginCommand as ProtobufPluginCommand, PluginMessagePayload,
|
||||
RequestPluginPermissionPayload, ResizePayload, RunCommandPayload, SetTimeoutPayload,
|
||||
SubscribePayload, SwitchSessionPayload, SwitchTabToPayload, UnsubscribePayload,
|
||||
WebRequestPayload,
|
||||
},
|
||||
plugin_permission::PermissionType as ProtobufPermissionType,
|
||||
resize::ResizeAction as ProtobufResizeAction,
|
||||
};
|
||||
|
||||
use crate::data::{ConnectToSession, PermissionType, PluginCommand};
|
||||
use crate::data::{ConnectToSession, HttpVerb, PermissionType, PluginCommand};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::path::PathBuf;
|
||||
|
||||
impl Into<HttpVerb> for ProtobufHttpVerb {
|
||||
fn into(self) -> HttpVerb {
|
||||
match self {
|
||||
ProtobufHttpVerb::Get => HttpVerb::Get,
|
||||
ProtobufHttpVerb::Post => HttpVerb::Post,
|
||||
ProtobufHttpVerb::Put => HttpVerb::Put,
|
||||
ProtobufHttpVerb::Delete => HttpVerb::Delete,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ProtobufHttpVerb> for HttpVerb {
|
||||
fn into(self) -> ProtobufHttpVerb {
|
||||
match self {
|
||||
HttpVerb::Get => ProtobufHttpVerb::Get,
|
||||
HttpVerb::Post => ProtobufHttpVerb::Post,
|
||||
HttpVerb::Put => ProtobufHttpVerb::Put,
|
||||
HttpVerb::Delete => ProtobufHttpVerb::Delete,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
||||
type Error = &'static str;
|
||||
fn try_from(protobuf_plugin_command: ProtobufPluginCommand) -> Result<Self, &'static str> {
|
||||
|
|
@ -577,6 +600,34 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
|||
},
|
||||
_ => Err("Mismatched payload for RunCommand"),
|
||||
},
|
||||
Some(CommandName::WebRequest) => match protobuf_plugin_command.payload {
|
||||
Some(Payload::WebRequestPayload(web_request_payload)) => {
|
||||
let context: BTreeMap<String, String> = web_request_payload
|
||||
.context
|
||||
.into_iter()
|
||||
.map(|e| (e.name, e.value))
|
||||
.collect();
|
||||
let headers: BTreeMap<String, String> = web_request_payload
|
||||
.headers
|
||||
.into_iter()
|
||||
.map(|e| (e.name, e.value))
|
||||
.collect();
|
||||
let verb = match ProtobufHttpVerb::from_i32(web_request_payload.verb) {
|
||||
Some(verb) => verb.into(),
|
||||
None => {
|
||||
return Err("Unrecognized http verb");
|
||||
},
|
||||
};
|
||||
Ok(PluginCommand::WebRequest(
|
||||
web_request_payload.url,
|
||||
verb,
|
||||
headers,
|
||||
web_request_payload.body,
|
||||
context,
|
||||
))
|
||||
},
|
||||
_ => Err("Mismatched payload for WebRequest"),
|
||||
},
|
||||
None => Err("Unrecognized plugin command"),
|
||||
}
|
||||
}
|
||||
|
|
@ -972,6 +1023,27 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
|
|||
})),
|
||||
})
|
||||
},
|
||||
PluginCommand::WebRequest(url, verb, headers, body, context) => {
|
||||
let context: Vec<_> = context
|
||||
.into_iter()
|
||||
.map(|(name, value)| ContextItem { name, value })
|
||||
.collect();
|
||||
let headers: Vec<_> = headers
|
||||
.into_iter()
|
||||
.map(|(name, value)| Header { name, value })
|
||||
.collect();
|
||||
let verb: ProtobufHttpVerb = verb.into();
|
||||
Ok(ProtobufPluginCommand {
|
||||
name: CommandName::WebRequest as i32,
|
||||
payload: Some(Payload::WebRequestPayload(WebRequestPayload {
|
||||
url,
|
||||
verb: verb as i32,
|
||||
body,
|
||||
headers,
|
||||
context,
|
||||
})),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ enum PermissionType {
|
|||
RunCommands = 3;
|
||||
OpenTerminalsOrPlugins = 4;
|
||||
WriteToStdin = 5;
|
||||
WebAccess = 6;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ impl TryFrom<ProtobufPermissionType> for PermissionType {
|
|||
Ok(PermissionType::OpenTerminalsOrPlugins)
|
||||
},
|
||||
ProtobufPermissionType::WriteToStdin => Ok(PermissionType::WriteToStdin),
|
||||
ProtobufPermissionType::WebAccess => Ok(PermissionType::WebAccess),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +40,7 @@ impl TryFrom<PermissionType> for ProtobufPermissionType {
|
|||
Ok(ProtobufPermissionType::OpenTerminalsOrPlugins)
|
||||
},
|
||||
PermissionType::WriteToStdin => Ok(ProtobufPermissionType::WriteToStdin),
|
||||
PermissionType::WebAccess => Ok(ProtobufPermissionType::WebAccess),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue