mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-26 04:25:05 +01:00
Recursive environments
This commit is contained in:
@@ -18,6 +18,7 @@ use tauri::{Manager, WebviewWindow};
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::watch::Receiver;
|
||||
|
||||
use crate::render::variables_from_environment;
|
||||
use crate::{models, render, response_err};
|
||||
|
||||
pub async fn send_http_request(
|
||||
@@ -33,8 +34,9 @@ pub async fn send_http_request(
|
||||
let workspace = models::get_workspace(window, &request.workspace_id)
|
||||
.await
|
||||
.expect("Failed to get Workspace");
|
||||
let vars = variables_from_environment(&workspace, environment_ref);
|
||||
|
||||
let mut url_string = render::render(&request.url, &workspace, environment.as_ref());
|
||||
let mut url_string = render::render(&request.url, &vars);
|
||||
|
||||
url_string = ensure_proto(&url_string);
|
||||
if !url_string.starts_with("http://") && !url_string.starts_with("https://") {
|
||||
@@ -144,8 +146,8 @@ pub async fn send_http_request(
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = render::render(&h.name, &workspace, environment_ref);
|
||||
let value = render::render(&h.value, &workspace, environment_ref);
|
||||
let name = render::render(&h.name, &vars);
|
||||
let value = render::render(&h.value, &vars);
|
||||
|
||||
let header_name = match HeaderName::from_bytes(name.as_bytes()) {
|
||||
Ok(n) => n,
|
||||
@@ -180,8 +182,8 @@ pub async fn send_http_request(
|
||||
.unwrap_or(empty_value)
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let username = render::render(raw_username, &workspace, environment_ref);
|
||||
let password = render::render(raw_password, &workspace, environment_ref);
|
||||
let username = render::render(raw_username, &vars);
|
||||
let password = render::render(raw_password, &vars);
|
||||
|
||||
let auth = format!("{username}:{password}");
|
||||
let encoded = base64::engine::general_purpose::STANDARD_NO_PAD.encode(auth);
|
||||
@@ -191,7 +193,7 @@ pub async fn send_http_request(
|
||||
);
|
||||
} else if b == "bearer" {
|
||||
let raw_token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or("");
|
||||
let token = render::render(raw_token, &workspace, environment_ref);
|
||||
let token = render::render(raw_token, &vars);
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(&format!("Bearer {token}")).unwrap(),
|
||||
@@ -205,8 +207,8 @@ pub async fn send_http_request(
|
||||
continue;
|
||||
}
|
||||
query_params.push((
|
||||
render::render(&p.name, &workspace, environment_ref),
|
||||
render::render(&p.value, &workspace, environment_ref),
|
||||
render::render(&p.name, &vars),
|
||||
render::render(&p.value, &vars),
|
||||
));
|
||||
}
|
||||
request_builder = request_builder.query(&query_params);
|
||||
@@ -222,7 +224,7 @@ pub async fn send_http_request(
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let body = render::render(raw_text, &workspace, environment_ref);
|
||||
let body = render::render(raw_text, &vars);
|
||||
request_builder = request_builder.body(body);
|
||||
} else if body_type == "application/x-www-form-urlencoded"
|
||||
&& request_body.contains_key("form")
|
||||
@@ -249,10 +251,7 @@ pub async fn send_http_request(
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
form_params.push((
|
||||
render::render(name, &workspace, environment_ref),
|
||||
render::render(value, &workspace, environment_ref),
|
||||
));
|
||||
form_params.push((render::render(name, &vars), render::render(value, &vars)));
|
||||
}
|
||||
}
|
||||
request_builder = request_builder.form(&form_params);
|
||||
@@ -301,13 +300,9 @@ pub async fn send_http_request(
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
|
||||
let name = render::render(name_raw, &workspace, environment_ref);
|
||||
let name = render::render(name_raw, &vars);
|
||||
let mut part = if file_path.is_empty() {
|
||||
multipart::Part::text(render::render(
|
||||
value_raw,
|
||||
&workspace,
|
||||
environment_ref,
|
||||
))
|
||||
multipart::Part::text(render::render(value_raw, &vars))
|
||||
} else {
|
||||
match fs::read(file_path) {
|
||||
Ok(f) => multipart::Part::bytes(f),
|
||||
@@ -324,7 +319,7 @@ pub async fn send_http_request(
|
||||
.unwrap_or_default();
|
||||
|
||||
if !ct_raw.is_empty() {
|
||||
let content_type = render::render(ct_raw, &workspace, environment_ref);
|
||||
let content_type = render::render(ct_raw, &vars);
|
||||
part = part
|
||||
.mime_str(content_type.as_str())
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
@@ -57,7 +57,7 @@ use crate::plugin::{
|
||||
find_plugins, get_plugin, ImportResult, PluginCapability,
|
||||
run_plugin_export_curl, run_plugin_filter, run_plugin_import,
|
||||
};
|
||||
use crate::render::render_request;
|
||||
use crate::render::{render_request, variables_from_environment};
|
||||
use crate::updates::{UpdateMode, YaakUpdater};
|
||||
use crate::window_menu::app_menu;
|
||||
|
||||
@@ -176,6 +176,7 @@ async fn cmd_grpc_go(
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut metadata = HashMap::new();
|
||||
let vars = variables_from_environment(&workspace, environment.as_ref());
|
||||
|
||||
// Add rest of metadata
|
||||
for h in req.clone().metadata.0 {
|
||||
@@ -187,15 +188,14 @@ async fn cmd_grpc_go(
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = render::render(&h.name, &workspace, environment.as_ref());
|
||||
let value = render::render(&h.value, &workspace, environment.as_ref());
|
||||
let name = render::render(&h.name, &vars);
|
||||
let value = render::render(&h.value, &vars);
|
||||
|
||||
metadata.insert(name, value);
|
||||
}
|
||||
|
||||
if let Some(b) = &req.authentication_type {
|
||||
let req = req.clone();
|
||||
let environment_ref = environment.as_ref();
|
||||
let empty_value = &serde_json::to_value("").unwrap();
|
||||
let a = req.authentication.0;
|
||||
|
||||
@@ -210,15 +210,15 @@ async fn cmd_grpc_go(
|
||||
.unwrap_or(empty_value)
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let username = render::render(raw_username, &workspace, environment_ref);
|
||||
let password = render::render(raw_password, &workspace, environment_ref);
|
||||
let username = render::render(raw_username, &vars);
|
||||
let password = render::render(raw_password, &vars);
|
||||
|
||||
let auth = format!("{username}:{password}");
|
||||
let encoded = base64::engine::general_purpose::STANDARD_NO_PAD.encode(auth);
|
||||
metadata.insert("Authorization".to_string(), format!("Basic {}", encoded));
|
||||
} else if b == "bearer" {
|
||||
let raw_token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or("");
|
||||
let token = render::render(raw_token, &workspace, environment_ref);
|
||||
let token = render::render(raw_token, &vars);
|
||||
metadata.insert("Authorization".to_string(), format!("Bearer {token}"));
|
||||
}
|
||||
}
|
||||
@@ -290,11 +290,10 @@ async fn cmd_grpc_go(
|
||||
|
||||
let cb = {
|
||||
let cancelled_rx = cancelled_rx.clone();
|
||||
let environment = environment.clone();
|
||||
let workspace = workspace.clone();
|
||||
let w = w.clone();
|
||||
let base_msg = base_msg.clone();
|
||||
let method_desc = method_desc.clone();
|
||||
let vars = vars.clone();
|
||||
|
||||
move |ev: tauri::Event| {
|
||||
if *cancelled_rx.borrow() {
|
||||
@@ -317,9 +316,8 @@ async fn cmd_grpc_go(
|
||||
Ok(IncomingMsg::Message(raw_msg)) => {
|
||||
let w = w.clone();
|
||||
let base_msg = base_msg.clone();
|
||||
let environment_ref = environment.as_ref();
|
||||
let method_desc = method_desc.clone();
|
||||
let msg = render::render(raw_msg.as_str(), &workspace, environment_ref);
|
||||
let msg = render::render(raw_msg.as_str(), &vars);
|
||||
let d_msg: DynamicMessage = match deserialize_message(msg.as_str(), method_desc)
|
||||
{
|
||||
Ok(d_msg) => d_msg,
|
||||
@@ -371,14 +369,13 @@ async fn cmd_grpc_go(
|
||||
let w = w.clone();
|
||||
let base_event = base_msg.clone();
|
||||
let req = req.clone();
|
||||
let workspace = workspace.clone();
|
||||
let environment = environment.clone();
|
||||
let vars = vars.clone();
|
||||
let raw_msg = if req.message.is_empty() {
|
||||
"{}".to_string()
|
||||
} else {
|
||||
req.message
|
||||
};
|
||||
let msg = render::render(&raw_msg, &workspace, environment.as_ref());
|
||||
let msg = render::render(&raw_msg, &vars);
|
||||
|
||||
upsert_grpc_event(
|
||||
&w,
|
||||
|
||||
@@ -9,16 +9,18 @@ use templates::parse_and_render;
|
||||
|
||||
pub fn render_request(r: &HttpRequest, w: &Workspace, e: Option<&Environment>) -> HttpRequest {
|
||||
let r = r.clone();
|
||||
let vars = &variables_from_environment(w, e);
|
||||
|
||||
HttpRequest {
|
||||
url: render(r.url.as_str(), w, e),
|
||||
url: render(r.url.as_str(), vars),
|
||||
url_parameters: Json(
|
||||
r.url_parameters
|
||||
.0
|
||||
.iter()
|
||||
.map(|p| HttpUrlParameter {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), w, e),
|
||||
value: render(p.value.as_str(), w, e),
|
||||
name: render(p.name.as_str(), vars),
|
||||
value: render(p.value.as_str(), vars),
|
||||
})
|
||||
.collect::<Vec<HttpUrlParameter>>(),
|
||||
),
|
||||
@@ -28,8 +30,8 @@ pub fn render_request(r: &HttpRequest, w: &Workspace, e: Option<&Environment>) -
|
||||
.iter()
|
||||
.map(|p| HttpRequestHeader {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), w, e),
|
||||
value: render(p.value.as_str(), w, e),
|
||||
name: render(p.name.as_str(), vars),
|
||||
value: render(p.value.as_str(), vars),
|
||||
})
|
||||
.collect::<Vec<HttpRequestHeader>>(),
|
||||
),
|
||||
@@ -39,11 +41,11 @@ pub fn render_request(r: &HttpRequest, w: &Workspace, e: Option<&Environment>) -
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let v = if v.is_string() {
|
||||
render(v.as_str().unwrap(), w, e)
|
||||
render(v.as_str().unwrap(), vars)
|
||||
} else {
|
||||
v.to_string()
|
||||
};
|
||||
(render(k, w, e), JsonValue::from(v))
|
||||
(render(k, vars), JsonValue::from(v))
|
||||
})
|
||||
.collect::<HashMap<String, JsonValue>>(),
|
||||
),
|
||||
@@ -53,11 +55,11 @@ pub fn render_request(r: &HttpRequest, w: &Workspace, e: Option<&Environment>) -
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let v = if v.is_string() {
|
||||
render(v.as_str().unwrap(), w, e)
|
||||
render(v.as_str().unwrap(), vars)
|
||||
} else {
|
||||
v.to_string()
|
||||
};
|
||||
(render(k, w, e), JsonValue::from(v))
|
||||
(render(k, vars), JsonValue::from(v))
|
||||
})
|
||||
.collect::<HashMap<String, JsonValue>>(),
|
||||
),
|
||||
@@ -65,7 +67,31 @@ pub fn render_request(r: &HttpRequest, w: &Workspace, e: Option<&Environment>) -
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(template: &str, workspace: &Workspace, environment: Option<&Environment>) -> String {
|
||||
pub fn recursively_render_variables<'s>(
|
||||
m: &HashMap<String, String>,
|
||||
render_count: usize,
|
||||
) -> HashMap<String, String> {
|
||||
let mut did_render = false;
|
||||
let mut new_map = m.clone();
|
||||
for (k, v) in m.clone() {
|
||||
let rendered = render(v.as_str(), m);
|
||||
if rendered != v {
|
||||
did_render = true
|
||||
}
|
||||
new_map.insert(k, rendered);
|
||||
}
|
||||
|
||||
if did_render && render_count <= 3 {
|
||||
new_map = recursively_render_variables(&new_map, render_count + 1);
|
||||
}
|
||||
|
||||
new_map
|
||||
}
|
||||
|
||||
pub fn variables_from_environment(
|
||||
workspace: &Workspace,
|
||||
environment: Option<&Environment>,
|
||||
) -> HashMap<String, String> {
|
||||
let mut variables = HashMap::new();
|
||||
variables = add_variable_to_map(variables, &workspace.variables.0);
|
||||
|
||||
@@ -73,13 +99,17 @@ pub fn render(template: &str, workspace: &Workspace, environment: Option<&Enviro
|
||||
variables = add_variable_to_map(variables, &e.variables.0);
|
||||
}
|
||||
|
||||
parse_and_render(template, variables, None)
|
||||
recursively_render_variables(&variables, 0)
|
||||
}
|
||||
|
||||
fn add_variable_to_map<'a>(
|
||||
m: HashMap<&'a str, &'a str>,
|
||||
variables: &'a Vec<EnvironmentVariable>,
|
||||
) -> HashMap<&'a str, &'a str> {
|
||||
pub fn render(template: &str, vars: &HashMap<String, String>) -> String {
|
||||
parse_and_render(template, vars, None)
|
||||
}
|
||||
|
||||
fn add_variable_to_map(
|
||||
m: HashMap<String, String>,
|
||||
variables: &Vec<EnvironmentVariable>,
|
||||
) -> HashMap<String, String> {
|
||||
let mut map = m.clone();
|
||||
for variable in variables {
|
||||
if !variable.enabled || variable.value.is_empty() {
|
||||
@@ -87,7 +117,7 @@ fn add_variable_to_map<'a>(
|
||||
}
|
||||
let name = variable.name.as_str();
|
||||
let value = variable.value.as_str();
|
||||
map.insert(name, value);
|
||||
map.insert(name.into(), value.into());
|
||||
}
|
||||
|
||||
map
|
||||
|
||||
@@ -6,7 +6,7 @@ type TemplateCallback = fn(name: &str, args: Vec<String>) -> String;
|
||||
|
||||
pub fn parse_and_render(
|
||||
template: &str,
|
||||
vars: HashMap<&str, &str>,
|
||||
vars: &HashMap<String, String>,
|
||||
cb: Option<TemplateCallback>,
|
||||
) -> String {
|
||||
let mut p = Parser::new(template);
|
||||
@@ -16,7 +16,7 @@ pub fn parse_and_render(
|
||||
|
||||
pub fn render(
|
||||
tokens: Vec<Token>,
|
||||
vars: HashMap<&str, &str>,
|
||||
vars: &HashMap<String, String>,
|
||||
cb: Option<TemplateCallback>,
|
||||
) -> String {
|
||||
let mut doc_str: Vec<String> = Vec::new();
|
||||
@@ -24,7 +24,7 @@ pub fn render(
|
||||
for t in tokens {
|
||||
match t {
|
||||
Token::Raw(s) => doc_str.push(s),
|
||||
Token::Tag(val) => doc_str.push(render_tag(val, vars.clone(), cb)),
|
||||
Token::Tag(val) => doc_str.push(render_tag(val, &vars, cb)),
|
||||
Token::Eof => {}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,9 @@ pub fn render(
|
||||
return doc_str.join("");
|
||||
}
|
||||
|
||||
fn render_tag<'s>(
|
||||
fn render_tag(
|
||||
val: Val,
|
||||
vars: HashMap<&'s str, &'s str>,
|
||||
vars: &HashMap<String, String>,
|
||||
cb: Option<TemplateCallback>,
|
||||
) -> String {
|
||||
match val {
|
||||
@@ -44,13 +44,13 @@ fn render_tag<'s>(
|
||||
None => "".into(),
|
||||
},
|
||||
Val::Fn { name, args } => {
|
||||
let empty = &"";
|
||||
let empty = "".to_string();
|
||||
let resolved_args = args
|
||||
.iter()
|
||||
.map(|a| match a {
|
||||
Val::Str(s) => s.to_string(),
|
||||
Val::Var(i) => vars.get(i.as_str()).unwrap_or(empty).to_string(),
|
||||
val => render_tag(val.clone(), vars.clone(), cb),
|
||||
Val::Var(i) => vars.get(i.as_str()).unwrap_or(&empty).to_string(),
|
||||
val => render_tag(val.clone(), vars, cb),
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
match cb {
|
||||
@@ -72,7 +72,7 @@ mod tests {
|
||||
let template = "";
|
||||
let vars = HashMap::new();
|
||||
let result = "";
|
||||
assert_eq!(parse_and_render(template, vars, None), result.to_string());
|
||||
assert_eq!(parse_and_render(template, &vars, None), result.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -80,23 +80,23 @@ mod tests {
|
||||
let template = "Hello World!";
|
||||
let vars = HashMap::new();
|
||||
let result = "Hello World!";
|
||||
assert_eq!(parse_and_render(template, vars, None), result.to_string());
|
||||
assert_eq!(parse_and_render(template, &vars, None), result.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_simple() {
|
||||
let template = "${[ foo ]}";
|
||||
let vars = HashMap::from([("foo", "bar")]);
|
||||
let vars = HashMap::from([("foo".to_string(), "bar".to_string())]);
|
||||
let result = "bar";
|
||||
assert_eq!(parse_and_render(template, vars, None), result.to_string());
|
||||
assert_eq!(parse_and_render(template, &vars, None), result.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_surrounded() {
|
||||
let template = "hello ${[ word ]} world!";
|
||||
let vars = HashMap::from([("word", "cruel")]);
|
||||
let vars = HashMap::from([("word".to_string(), "cruel".to_string())]);
|
||||
let result = "hello cruel world!";
|
||||
assert_eq!(parse_and_render(template, vars, None), result.to_string());
|
||||
assert_eq!(parse_and_render(template, &vars, None), result.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -108,7 +108,7 @@ mod tests {
|
||||
fn cb(name: &str, args: Vec<String>) -> String {
|
||||
format!("{name}: {:?}", args)
|
||||
}
|
||||
assert_eq!(parse_and_render(template, vars, Some(cb)), result);
|
||||
assert_eq!(parse_and_render(template, &vars, Some(cb)), result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -125,7 +125,7 @@ mod tests {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
parse_and_render(template, vars, Some(cb)),
|
||||
parse_and_render(template, &vars, Some(cb)),
|
||||
result.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ const EnvironmentEditor = function ({
|
||||
namePlaceholder="VAR_NAME"
|
||||
nameValidate={validateName}
|
||||
valueType={valueVisibility.value ? 'text' : 'password'}
|
||||
valueAutocompleteVariables={false}
|
||||
valueAutocompleteVariables={true}
|
||||
forceUpdateKey={environment?.id ?? workspace?.id ?? 'n/a'}
|
||||
pairs={variables}
|
||||
onChange={handleChange}
|
||||
|
||||
Reference in New Issue
Block a user