mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-19 17:17:50 +01:00
Async template functions working
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::Write;
|
||||
@@ -6,8 +7,8 @@ use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::render::variables_from_environment;
|
||||
use crate::{render, response_err};
|
||||
use crate::render::render_request;
|
||||
use crate::response_err;
|
||||
use base64::Engine;
|
||||
use http::header::{ACCEPT, USER_AGENT};
|
||||
use http::{HeaderMap, HeaderName, HeaderValue};
|
||||
@@ -16,6 +17,7 @@ use mime_guess::Mime;
|
||||
use reqwest::redirect::Policy;
|
||||
use reqwest::Method;
|
||||
use reqwest::{multipart, Url};
|
||||
use serde_json::Value;
|
||||
use tauri::{Manager, Runtime, WebviewWindow};
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::watch::Receiver;
|
||||
@@ -26,19 +28,18 @@ use yaak_models::queries::{get_workspace, update_response_if_id, upsert_cookie_j
|
||||
|
||||
pub async fn send_http_request<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
request: HttpRequest,
|
||||
request: &HttpRequest,
|
||||
response: &HttpResponse,
|
||||
environment: Option<Environment>,
|
||||
cookie_jar: Option<CookieJar>,
|
||||
cancel_rx: &mut Receiver<bool>,
|
||||
) -> Result<HttpResponse, String> {
|
||||
let environment_ref = environment.as_ref();
|
||||
let workspace = get_workspace(window, &request.workspace_id)
|
||||
.await
|
||||
.expect("Failed to get Workspace");
|
||||
let vars = variables_from_environment(&workspace, environment_ref);
|
||||
let rendered_request = render_request(&request, &workspace, environment.as_ref()).await;
|
||||
|
||||
let mut url_string = render::render(&request.url, &vars);
|
||||
let mut url_string = rendered_request.url;
|
||||
|
||||
url_string = ensure_proto(&url_string);
|
||||
if !url_string.starts_with("http://") && !url_string.starts_with("https://") {
|
||||
@@ -115,7 +116,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
}
|
||||
};
|
||||
|
||||
let m = Method::from_bytes(request.method.to_uppercase().as_bytes())
|
||||
let m = Method::from_bytes(rendered_request.method.to_uppercase().as_bytes())
|
||||
.expect("Failed to create method");
|
||||
let mut request_builder = client.request(m, url);
|
||||
|
||||
@@ -138,7 +139,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
// );
|
||||
// }
|
||||
|
||||
for h in request.headers {
|
||||
for h in rendered_request.headers {
|
||||
if h.name.is_empty() && h.value.is_empty() {
|
||||
continue;
|
||||
}
|
||||
@@ -147,17 +148,14 @@ pub async fn send_http_request<R: Runtime>(
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = render::render(&h.name, &vars);
|
||||
let value = render::render(&h.value, &vars);
|
||||
|
||||
let header_name = match HeaderName::from_bytes(name.as_bytes()) {
|
||||
let header_name = match HeaderName::from_bytes(h.name.as_bytes()) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
error!("Failed to create header name: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let header_value = match HeaderValue::from_str(value.as_str()) {
|
||||
let header_value = match HeaderValue::from_str(h.value.as_str()) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
error!("Failed to create header value: {}", e);
|
||||
@@ -168,23 +166,21 @@ pub async fn send_http_request<R: Runtime>(
|
||||
headers.insert(header_name, header_value);
|
||||
}
|
||||
|
||||
if let Some(b) = &request.authentication_type {
|
||||
if let Some(b) = &rendered_request.authentication_type {
|
||||
let empty_value = &serde_json::to_value("").unwrap();
|
||||
let a = request.authentication;
|
||||
let a = rendered_request.authentication;
|
||||
|
||||
if b == "basic" {
|
||||
let raw_username = a
|
||||
let username = a
|
||||
.get("username")
|
||||
.unwrap_or(empty_value)
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let raw_password = a
|
||||
.unwrap_or_default();
|
||||
let password = a
|
||||
.get("password")
|
||||
.unwrap_or(empty_value)
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let username = render::render(raw_username, &vars);
|
||||
let password = render::render(raw_password, &vars);
|
||||
.unwrap_or_default();
|
||||
|
||||
let auth = format!("{username}:{password}");
|
||||
let encoded = base64::engine::general_purpose::STANDARD_NO_PAD.encode(auth);
|
||||
@@ -193,8 +189,11 @@ pub async fn send_http_request<R: Runtime>(
|
||||
HeaderValue::from_str(&format!("Basic {}", encoded)).unwrap(),
|
||||
);
|
||||
} else if b == "bearer" {
|
||||
let raw_token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or("");
|
||||
let token = render::render(raw_token, &vars);
|
||||
let token = a
|
||||
.get("token")
|
||||
.unwrap_or(empty_value)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(&format!("Bearer {token}")).unwrap(),
|
||||
@@ -203,56 +202,38 @@ pub async fn send_http_request<R: Runtime>(
|
||||
}
|
||||
|
||||
let mut query_params = Vec::new();
|
||||
for p in request.url_parameters {
|
||||
for p in rendered_request.url_parameters {
|
||||
if !p.enabled || p.name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
query_params.push((
|
||||
render::render(&p.name, &vars),
|
||||
render::render(&p.value, &vars),
|
||||
));
|
||||
query_params.push((p.name, p.value));
|
||||
}
|
||||
request_builder = request_builder.query(&query_params);
|
||||
|
||||
if let Some(body_type) = &request.body_type {
|
||||
let empty_string = &serde_json::to_value("").unwrap();
|
||||
let empty_bool = &serde_json::to_value(false).unwrap();
|
||||
let request_body = request.body;
|
||||
|
||||
let request_body = rendered_request.body;
|
||||
if let Some(body_type) = &rendered_request.body_type {
|
||||
if request_body.contains_key("text") {
|
||||
let raw_text = request_body
|
||||
.get("text")
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let body = render::render(raw_text, &vars);
|
||||
request_builder = request_builder.body(body);
|
||||
let body = get_str_h(&request_body, "text");
|
||||
request_builder = request_builder.body(body.to_owned());
|
||||
} else if body_type == "application/x-www-form-urlencoded"
|
||||
&& request_body.contains_key("form")
|
||||
{
|
||||
let mut form_params = Vec::new();
|
||||
let form = request_body.get("form");
|
||||
if let Some(f) = form {
|
||||
for p in f.as_array().unwrap_or(&Vec::new()) {
|
||||
let enabled = p
|
||||
.get("enabled")
|
||||
.unwrap_or(empty_bool)
|
||||
.as_bool()
|
||||
.unwrap_or(false);
|
||||
let name = p
|
||||
.get("name")
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
if !enabled || name.is_empty() {
|
||||
continue;
|
||||
match f.as_array() {
|
||||
None => {}
|
||||
Some(a) => {
|
||||
for p in a {
|
||||
let enabled = get_bool(p, "enabled");
|
||||
let name = get_str(p, "name");
|
||||
if !enabled || name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let value = get_str(p, "value");
|
||||
form_params.push((name, value));
|
||||
}
|
||||
}
|
||||
let value = p
|
||||
.get("value")
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
form_params.push((render::render(name, &vars), render::render(value, &vars)));
|
||||
}
|
||||
}
|
||||
request_builder = request_builder.form(&form_params);
|
||||
@@ -274,77 +255,59 @@ pub async fn send_http_request<R: Runtime>(
|
||||
} else if body_type == "multipart/form-data" && request_body.contains_key("form") {
|
||||
let mut multipart_form = multipart::Form::new();
|
||||
if let Some(form_definition) = request_body.get("form") {
|
||||
for p in form_definition.as_array().unwrap_or(&Vec::new()) {
|
||||
let enabled = p
|
||||
.get("enabled")
|
||||
.unwrap_or(empty_bool)
|
||||
.as_bool()
|
||||
.unwrap_or(false);
|
||||
let name_raw = p
|
||||
.get("name")
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
match form_definition.as_array() {
|
||||
None => {}
|
||||
Some(fd) => {
|
||||
for p in fd {
|
||||
let enabled = get_bool(p, "enabled");
|
||||
let name = get_str(p, "name").to_string();
|
||||
|
||||
if !enabled || name_raw.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_path = p
|
||||
.get("file")
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
let value_raw = p
|
||||
.get("value")
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
|
||||
let name = render::render(name_raw, &vars);
|
||||
let mut part = if file_path.is_empty() {
|
||||
multipart::Part::text(render::render(value_raw, &vars))
|
||||
} else {
|
||||
match fs::read(file_path) {
|
||||
Ok(f) => multipart::Part::bytes(f),
|
||||
Err(e) => {
|
||||
return response_err(response, e.to_string(), window).await;
|
||||
if !enabled || name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_path = get_str(p, "file").to_owned();
|
||||
let value = get_str(p, "value").to_owned();
|
||||
|
||||
let mut part = if file_path.is_empty() {
|
||||
multipart::Part::text(value.clone())
|
||||
} else {
|
||||
match fs::read(file_path.clone()) {
|
||||
Ok(f) => multipart::Part::bytes(f),
|
||||
Err(e) => {
|
||||
return response_err(response, e.to_string(), window).await;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let content_type = get_str(p, "contentType");
|
||||
|
||||
// Set or guess mimetype
|
||||
if !content_type.is_empty() {
|
||||
part = part.mime_str(content_type).map_err(|e| e.to_string())?;
|
||||
} else if !file_path.is_empty() {
|
||||
let default_mime =
|
||||
Mime::from_str("application/octet-stream").unwrap();
|
||||
let mime =
|
||||
mime_guess::from_path(file_path.clone()).first_or(default_mime);
|
||||
part = part
|
||||
.mime_str(mime.essence_str())
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
// Set file path if not empty
|
||||
if !file_path.is_empty() {
|
||||
let filename = PathBuf::from(file_path)
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
part = part.file_name(filename);
|
||||
}
|
||||
|
||||
multipart_form = multipart_form.part(name, part);
|
||||
}
|
||||
};
|
||||
|
||||
let ct_raw = p
|
||||
.get("contentType")
|
||||
.unwrap_or(empty_string)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
|
||||
// Set or guess mimetype
|
||||
if !ct_raw.is_empty() {
|
||||
let content_type = render::render(ct_raw, &vars);
|
||||
part = part
|
||||
.mime_str(content_type.as_str())
|
||||
.map_err(|e| e.to_string())?;
|
||||
} else if !file_path.is_empty() {
|
||||
let default_mime = Mime::from_str("application/octet-stream").unwrap();
|
||||
let mime = mime_guess::from_path(file_path).first_or(default_mime);
|
||||
part = part
|
||||
.mime_str(mime.essence_str())
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
// Set fil path if not empty
|
||||
if !file_path.is_empty() {
|
||||
let filename = PathBuf::from(file_path)
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
part = part.file_name(filename);
|
||||
}
|
||||
|
||||
multipart_form = multipart_form.part(name, part);
|
||||
}
|
||||
}
|
||||
headers.remove("Content-Type"); // reqwest will add this automatically
|
||||
@@ -496,3 +459,24 @@ fn ensure_proto(url_str: &str) -> String {
|
||||
|
||||
format!("http://{url_str}")
|
||||
}
|
||||
|
||||
fn get_bool(v: &Value, key: &str) -> bool {
|
||||
match v.get(key) {
|
||||
None => false,
|
||||
Some(v) => v.as_bool().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_str<'a>(v: &'a Value, key: &str) -> &'a str {
|
||||
match v.get(key) {
|
||||
None => "",
|
||||
Some(v) => v.as_str().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_str_h<'a>(v: &'a HashMap<String, Value>, key: &str) -> &'a str {
|
||||
match v.get(key) {
|
||||
None => "",
|
||||
Some(v) => v.as_str().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,10 @@ use yaak_models::queries::{
|
||||
};
|
||||
use yaak_plugin_runtime::events::{
|
||||
CallHttpRequestActionRequest, FilterResponse, GetHttpRequestActionsResponse,
|
||||
GetHttpRequestByIdResponse, InternalEvent, InternalEventPayload, RenderHttpRequestResponse,
|
||||
SendHttpRequestResponse,
|
||||
GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, InternalEvent, InternalEventPayload,
|
||||
RenderHttpRequestResponse, SendHttpRequestResponse,
|
||||
};
|
||||
use yaak_templates::{parse_and_render, Parser, Tokens};
|
||||
use yaak_templates::{Parser, Tokens};
|
||||
|
||||
mod analytics;
|
||||
mod export_resources;
|
||||
@@ -128,7 +128,7 @@ async fn cmd_render_template(
|
||||
let workspace = get_workspace(&window, &workspace_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rendered = render_template(template, &workspace, environment.as_ref());
|
||||
let rendered = render_template(template, &workspace, environment.as_ref()).await;
|
||||
Ok(rendered)
|
||||
}
|
||||
|
||||
@@ -195,7 +195,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());
|
||||
let vars = variables_from_environment(&workspace, environment.as_ref()).await;
|
||||
|
||||
// Add rest of metadata
|
||||
for h in req.clone().metadata {
|
||||
@@ -207,8 +207,8 @@ async fn cmd_grpc_go(
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = render::render(&h.name, &vars);
|
||||
let value = render::render(&h.value, &vars);
|
||||
let name = render::render(&h.name, &vars).await;
|
||||
let value = render::render(&h.value, &vars).await;
|
||||
|
||||
metadata.insert(name, value);
|
||||
}
|
||||
@@ -229,15 +229,15 @@ async fn cmd_grpc_go(
|
||||
.unwrap_or(empty_value)
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let username = render::render(raw_username, &vars);
|
||||
let password = render::render(raw_password, &vars);
|
||||
let username = render::render(raw_username, &vars).await;
|
||||
let password = render::render(raw_password, &vars).await;
|
||||
|
||||
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, &vars);
|
||||
let token = render::render(raw_token, &vars).await;
|
||||
metadata.insert("Authorization".to_string(), format!("Bearer {token}"));
|
||||
}
|
||||
}
|
||||
@@ -355,7 +355,10 @@ async fn cmd_grpc_go(
|
||||
let w = w.clone();
|
||||
let base_msg = base_msg.clone();
|
||||
let method_desc = method_desc.clone();
|
||||
let msg = render::render(raw_msg.as_str(), &vars);
|
||||
let vars = vars.clone();
|
||||
let msg = tauri::async_runtime::block_on(async move {
|
||||
render::render(raw_msg.as_str(), &vars).await
|
||||
});
|
||||
let d_msg: DynamicMessage = match deserialize_message(msg.as_str(), method_desc)
|
||||
{
|
||||
Ok(d_msg) => d_msg,
|
||||
@@ -413,7 +416,7 @@ async fn cmd_grpc_go(
|
||||
} else {
|
||||
req.message
|
||||
};
|
||||
let msg = render::render(&raw_msg, &vars);
|
||||
let msg = render::render(&raw_msg, &vars).await;
|
||||
|
||||
upsert_grpc_event(
|
||||
&w,
|
||||
@@ -733,7 +736,7 @@ async fn cmd_send_ephemeral_request(
|
||||
|
||||
send_http_request(
|
||||
&window,
|
||||
request,
|
||||
&request,
|
||||
&response,
|
||||
environment,
|
||||
cookie_jar,
|
||||
@@ -914,6 +917,16 @@ async fn cmd_http_request_actions(
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_template_functions(
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> Result<Vec<GetTemplateFunctionsResponse>, String> {
|
||||
plugin_manager
|
||||
.run_template_functions()
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_call_http_request_action(
|
||||
req: CallHttpRequestActionRequest,
|
||||
@@ -1057,7 +1070,7 @@ async fn cmd_send_http_request(
|
||||
|
||||
send_http_request(
|
||||
&window,
|
||||
request.clone(),
|
||||
&request,
|
||||
&response,
|
||||
environment,
|
||||
cookie_jar,
|
||||
@@ -1692,6 +1705,7 @@ pub fn run() {
|
||||
cmd_grpc_go,
|
||||
cmd_grpc_reflect,
|
||||
cmd_http_request_actions,
|
||||
cmd_template_functions,
|
||||
cmd_import_data,
|
||||
cmd_list_cookie_jars,
|
||||
cmd_list_environments,
|
||||
@@ -1986,7 +2000,7 @@ async fn handle_plugin_event<R: Runtime>(
|
||||
Some(id) => get_environment(w, id.as_str()).await.ok(),
|
||||
};
|
||||
let rendered_http_request =
|
||||
render_request(&req.http_request, &workspace, environment.as_ref());
|
||||
render_request(&req.http_request, &workspace, environment.as_ref()).await;
|
||||
Some(InternalEventPayload::RenderHttpRequestResponse(
|
||||
RenderHttpRequestResponse {
|
||||
http_request: rendered_http_request,
|
||||
@@ -2025,7 +2039,7 @@ async fn handle_plugin_event<R: Runtime>(
|
||||
|
||||
let result = send_http_request(
|
||||
&w,
|
||||
req.http_request,
|
||||
&req.http_request,
|
||||
&resp,
|
||||
environment,
|
||||
cookie_jar,
|
||||
|
||||
@@ -4,73 +4,77 @@ use std::collections::HashMap;
|
||||
use yaak_models::models::{
|
||||
Environment, EnvironmentVariable, HttpRequest, HttpRequestHeader, HttpUrlParameter, Workspace,
|
||||
};
|
||||
use yaak_templates::parse_and_render;
|
||||
use yaak_templates::{parse_and_render, TemplateCallback};
|
||||
|
||||
pub fn render_template(template: &str, w: &Workspace, e: Option<&Environment>) -> String {
|
||||
let vars = &variables_from_environment(w, e);
|
||||
render(template, vars)
|
||||
pub async fn render_template(template: &str, w: &Workspace, e: Option<&Environment>) -> String {
|
||||
let vars = &variables_from_environment(w, e).await;
|
||||
render(template, vars).await
|
||||
}
|
||||
|
||||
pub fn render_request(r: &HttpRequest, w: &Workspace, e: Option<&Environment>) -> HttpRequest {
|
||||
pub async fn render_request(
|
||||
r: &HttpRequest,
|
||||
w: &Workspace,
|
||||
e: Option<&Environment>,
|
||||
) -> HttpRequest {
|
||||
let r = r.clone();
|
||||
let vars = &variables_from_environment(w, e);
|
||||
let vars = &variables_from_environment(w, e).await;
|
||||
|
||||
let mut url_parameters = Vec::new();
|
||||
for p in r.url_parameters {
|
||||
url_parameters.push(HttpUrlParameter {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), vars).await,
|
||||
value: render(p.value.as_str(), vars).await,
|
||||
})
|
||||
}
|
||||
|
||||
let mut headers = Vec::new();
|
||||
for p in r.headers {
|
||||
headers.push(HttpRequestHeader {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), vars).await,
|
||||
value: render(p.value.as_str(), vars).await,
|
||||
})
|
||||
}
|
||||
|
||||
let mut body = HashMap::new();
|
||||
for (k, v) in r.body {
|
||||
let v = if v.is_string() {
|
||||
render(v.as_str().unwrap(), vars).await
|
||||
} else {
|
||||
v.to_string()
|
||||
};
|
||||
body.insert(render(k.as_str(), vars).await, Value::from(v));
|
||||
}
|
||||
|
||||
let mut authentication = HashMap::new();
|
||||
for (k, v) in r.authentication {
|
||||
let v = if v.is_string() {
|
||||
render(v.as_str().unwrap(), vars).await
|
||||
} else {
|
||||
v.to_string()
|
||||
};
|
||||
authentication.insert(render(k.as_str(), vars).await, Value::from(v));
|
||||
}
|
||||
|
||||
HttpRequest {
|
||||
url: render(r.url.as_str(), vars),
|
||||
url_parameters: r
|
||||
.url_parameters
|
||||
.iter()
|
||||
.map(|p| HttpUrlParameter {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), vars),
|
||||
value: render(p.value.as_str(), vars),
|
||||
})
|
||||
.collect::<Vec<HttpUrlParameter>>(),
|
||||
headers: r
|
||||
.headers
|
||||
.iter()
|
||||
.map(|p| HttpRequestHeader {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), vars),
|
||||
value: render(p.value.as_str(), vars),
|
||||
})
|
||||
.collect::<Vec<HttpRequestHeader>>(),
|
||||
body: r
|
||||
.body
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let v = if v.is_string() {
|
||||
render(v.as_str().unwrap(), vars)
|
||||
} else {
|
||||
v.to_string()
|
||||
};
|
||||
(render(k, vars), Value::from(v))
|
||||
})
|
||||
.collect::<HashMap<String, Value>>(),
|
||||
authentication: r
|
||||
.authentication
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let v = if v.is_string() {
|
||||
render(v.as_str().unwrap(), vars)
|
||||
} else {
|
||||
v.to_string()
|
||||
};
|
||||
(render(k, vars), Value::from(v))
|
||||
})
|
||||
.collect::<HashMap<String, Value>>(),
|
||||
url: render(r.url.as_str(), vars).await,
|
||||
url_parameters,
|
||||
headers,
|
||||
body,
|
||||
authentication,
|
||||
..r
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recursively_render_variables<'s>(
|
||||
pub async 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);
|
||||
let rendered = Box::pin(render(v.as_str(), m)).await;
|
||||
if rendered != v {
|
||||
did_render = true
|
||||
}
|
||||
@@ -78,13 +82,13 @@ pub fn recursively_render_variables<'s>(
|
||||
}
|
||||
|
||||
if did_render && render_count <= 3 {
|
||||
new_map = recursively_render_variables(&new_map, render_count + 1);
|
||||
new_map = Box::pin(recursively_render_variables(&new_map, render_count + 1)).await;
|
||||
}
|
||||
|
||||
new_map
|
||||
}
|
||||
|
||||
pub fn variables_from_environment(
|
||||
pub async fn variables_from_environment(
|
||||
workspace: &Workspace,
|
||||
environment: Option<&Environment>,
|
||||
) -> HashMap<String, String> {
|
||||
@@ -95,17 +99,22 @@ pub fn variables_from_environment(
|
||||
variables = add_variable_to_map(variables, &e.variables);
|
||||
}
|
||||
|
||||
recursively_render_variables(&variables, 0)
|
||||
recursively_render_variables(&variables, 0).await
|
||||
}
|
||||
|
||||
pub fn render(template: &str, vars: &HashMap<String, String>) -> String {
|
||||
parse_and_render(template, vars, Some(template_callback))
|
||||
pub async fn render(template: &str, vars: &HashMap<String, String>) -> String {
|
||||
parse_and_render(template, vars, &Box::new(PluginTemplateCallback::default())).await
|
||||
}
|
||||
|
||||
fn template_callback(name: &str, args: HashMap<String, String>) -> Result<String, String> {
|
||||
match name {
|
||||
"timestamp" => timestamp(args),
|
||||
_ => Err(format!("Unknown template function {name}")),
|
||||
#[derive(Default)]
|
||||
struct PluginTemplateCallback {}
|
||||
|
||||
impl TemplateCallback for PluginTemplateCallback {
|
||||
async fn run(&self, fn_name: &str, args: HashMap<String, String>) -> Result<String, String> {
|
||||
match fn_name {
|
||||
"timestamp" => timestamp(args),
|
||||
_ => Err(format!("Unknown template function {fn_name}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user