A bunch of improvements to chaining

This commit is contained in:
Gregory Schier
2024-08-19 14:10:44 -07:00
parent 3411575ecc
commit bd02206df2
23 changed files with 218 additions and 82 deletions

View File

@@ -52,14 +52,14 @@ use yaak_models::queries::{
get_grpc_request, get_http_request, get_http_response, get_key_value_raw,
get_or_create_settings, get_workspace, list_cookie_jars, list_environments, list_folders,
list_grpc_connections, list_grpc_events, list_grpc_requests, list_http_requests,
list_responses, list_workspaces, set_key_value_raw, update_response_if_id, update_settings,
upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection,
list_http_responses, list_workspaces, set_key_value_raw, update_response_if_id,
update_settings, upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection,
upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_workspace,
};
use yaak_plugin_runtime::events::{
CallHttpRequestActionRequest, FilterResponse, GetHttpRequestActionsResponse,
GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, InternalEvent, InternalEventPayload,
RenderHttpRequestResponse, SendHttpRequestResponse,
CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse,
InternalEvent, InternalEventPayload, RenderHttpRequestResponse, SendHttpRequestResponse,
};
use yaak_templates::{Parser, Tokens};
@@ -1496,7 +1496,7 @@ async fn cmd_list_http_responses(
limit: Option<i64>,
w: WebviewWindow,
) -> Result<Vec<HttpResponse>, String> {
list_responses(&w, request_id, limit)
list_http_responses(&w, request_id, limit)
.await
.map_err(|e| e.to_string())
}
@@ -1960,12 +1960,6 @@ async fn handle_plugin_event<R: Runtime>(
event: &InternalEvent,
) -> Option<InternalEventPayload> {
let event = match event.clone().payload {
InternalEventPayload::GetHttpRequestByIdRequest(req) => {
let http_request = get_http_request(app_handle, req.id.as_str()).await.ok();
Some(InternalEventPayload::GetHttpRequestByIdResponse(
GetHttpRequestByIdResponse { http_request },
))
}
InternalEventPayload::CopyTextRequest(req) => {
app_handle
.clipboard()
@@ -1979,6 +1973,21 @@ async fn handle_plugin_event<R: Runtime>(
.expect("Failed to emit show_toast");
None
}
InternalEventPayload::FindHttpResponsesRequest(req) => {
let http_responses =
list_http_responses(app_handle, req.request_id.as_str(), req.limit)
.await
.unwrap_or_default();
Some(InternalEventPayload::FindHttpResponsesResponse(
FindHttpResponsesResponse { http_responses },
))
}
InternalEventPayload::GetHttpRequestByIdRequest(req) => {
let http_request = get_http_request(app_handle, req.id.as_str()).await.ok();
Some(InternalEventPayload::GetHttpRequestByIdResponse(
GetHttpRequestByIdResponse { http_request },
))
}
InternalEventPayload::RenderHttpRequestRequest(req) => {
let webview_windows = app_handle.get_focused_window()?.webview_windows();
let w = match webview_windows.iter().next() {

View File

@@ -1,8 +1,11 @@
use crate::template_callback::PluginTemplateCallback;
use serde_json::Value;
use serde_json::{json, Map, Value};
use std::collections::HashMap;
use tauri::{AppHandle, Manager, Runtime};
use yaak_models::models::{Environment, EnvironmentVariable, GrpcMetadataEntry, GrpcRequest, HttpRequest, HttpRequestHeader, HttpUrlParameter, Workspace};
use yaak_models::models::{
Environment, EnvironmentVariable, GrpcMetadataEntry, GrpcRequest, HttpRequest,
HttpRequestHeader, HttpUrlParameter, Workspace,
};
use yaak_templates::{parse_and_render, TemplateCallback};
pub async fn render_template<R: Runtime>(
@@ -36,17 +39,12 @@ pub async fn render_grpc_request<R: Runtime>(
let mut authentication = HashMap::new();
for (k, v) in r.authentication.clone() {
let v = if v.is_string() {
render(v.as_str().unwrap(), vars, cb).await
} else {
v.to_string()
};
authentication.insert(render(k.as_str(), vars, cb).await, Value::from(v));
authentication.insert(k, render_json_value(v, vars, cb).await);
}
let url = render(r.url.as_str(), vars, cb).await;
GrpcRequest{
GrpcRequest {
url,
metadata,
authentication,
@@ -83,22 +81,12 @@ pub async fn render_http_request<R: Runtime>(
let mut body = HashMap::new();
for (k, v) in r.body.clone() {
let v = if v.is_string() {
render(v.as_str().unwrap(), vars, cb).await
} else {
v.to_string()
};
body.insert(render(k.as_str(), vars, cb).await, Value::from(v));
body.insert(k, render_json_value(v, vars, cb).await);
}
let mut authentication = HashMap::new();
for (k, v) in r.authentication.clone() {
let v = if v.is_string() {
render(v.as_str().unwrap(), vars, cb).await
} else {
v.to_string()
};
authentication.insert(render(k.as_str(), vars, cb).await, Value::from(v));
authentication.insert(k, render_json_value(v, vars, cb).await);
}
let url = render(r.url.clone().as_str(), vars, cb).await;
@@ -173,3 +161,103 @@ fn add_variable_to_map(
map
}
pub async fn render_json_value<T: TemplateCallback>(
v: Value,
vars: &HashMap<String, String>,
cb: &T,
) -> Value {
match v {
Value::String(s) => json!(render(s.as_str(), vars, cb).await),
Value::Array(a) => {
let mut new_a = Vec::new();
for v in a {
new_a.push(Box::pin(render_json_value(v, vars, cb)).await)
}
json!(new_a)
}
Value::Object(o) => {
let mut new_o = Map::new();
for (k, v) in o {
let key = Box::pin(render(k.as_str(), vars, cb)).await;
let value = Box::pin(render_json_value(v, vars, cb)).await;
new_o.insert(key, value);
}
json!(new_o)
}
v => v,
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use std::collections::HashMap;
use yaak_templates::TemplateCallback;
struct EmptyCB {}
impl TemplateCallback for EmptyCB {
async fn run(
&self,
_fn_name: &str,
_args: HashMap<String, String>,
) -> Result<String, String> {
todo!()
}
}
#[tokio::test]
async fn render_json_value_string() {
let v = json!("${[a]}");
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
assert_eq!(result, json!("aaa"))
}
#[tokio::test]
async fn render_json_value_array() {
let v = json!(["${[a]}", "${[a]}"]);
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
assert_eq!(result, json!(["aaa", "aaa"]))
}
#[tokio::test]
async fn render_json_value_object() {
let v = json!({"${[a]}": "${[a]}"});
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
assert_eq!(result, json!({"aaa": "aaa"}))
}
#[tokio::test]
async fn render_json_value_nested() {
let v = json!([
123,
{"${[a]}": "${[a]}"},
null,
"${[a]}",
false,
{"x": ["${[a]}"]}
]);
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
assert_eq!(result, json!([
123,
{"aaa": "aaa"},
null,
"aaa",
false,
{"x": ["aaa"]}
]))
}
}

View File

@@ -720,6 +720,8 @@ pub async fn delete_environment<R: Runtime>(
emit_deleted_model(window, env)
}
const SETTINGS_ID: &str = "default";
async fn get_settings<R: Runtime>(mgr: &impl Manager<R>) -> Result<Settings> {
let dbm = &*mgr.state::<SqliteConnection>();
let db = dbm.0.lock().await.get().unwrap();
@@ -727,7 +729,7 @@ async fn get_settings<R: Runtime>(mgr: &impl Manager<R>) -> Result<Settings> {
let (sql, params) = Query::select()
.from(SettingsIden::Table)
.column(Asterisk)
.cond_where(Expr::col(SettingsIden::Id).eq("default"))
.cond_where(Expr::col(SettingsIden::Id).eq(SETTINGS_ID))
.build_rusqlite(SqliteQueryBuilder);
let mut stmt = db.prepare(sql.as_str())?;
Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?)
@@ -744,7 +746,7 @@ pub async fn get_or_create_settings<R: Runtime>(mgr: &impl Manager<R>) -> Settin
let (sql, params) = Query::insert()
.into_table(SettingsIden::Table)
.columns([SettingsIden::Id])
.values_panic(["default".into()])
.values_panic([SETTINGS_ID.into()])
.returning_all()
.build_rusqlite(SqliteQueryBuilder);
@@ -1324,13 +1326,13 @@ pub async fn delete_all_http_responses<R: Runtime>(
window: &WebviewWindow<R>,
request_id: &str,
) -> Result<()> {
for r in list_responses(window, request_id, None).await? {
for r in list_http_responses(window, request_id, None).await? {
delete_http_response(window, &r.id).await?;
}
Ok(())
}
pub async fn list_responses<R: Runtime>(
pub async fn list_http_responses<R: Runtime>(
mgr: &impl Manager<R>,
request_id: &str,
limit: Option<i64>,

View File

@@ -54,6 +54,9 @@ pub enum InternalEventPayload {
GetHttpRequestByIdRequest(GetHttpRequestByIdRequest),
GetHttpRequestByIdResponse(GetHttpRequestByIdResponse),
FindHttpResponsesRequest(FindHttpResponsesRequest),
FindHttpResponsesResponse(FindHttpResponsesResponse),
/// Returned when a plugin doesn't get run, just so the server
/// has something to listen for
@@ -347,6 +350,21 @@ pub struct GetHttpRequestByIdResponse {
pub http_request: Option<HttpRequest>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export)]
pub struct FindHttpResponsesRequest {
pub request_id: String,
pub limit: Option<i64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export)]
pub struct FindHttpResponsesResponse {
pub http_responses: Vec<HttpResponse>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export)]

View File

@@ -169,9 +169,10 @@ impl PluginManager {
content: &str,
content_type: &str,
) -> Result<FilterResponse> {
let plugin_name = match content_type {
"application/json" => "filter-jsonpath",
_ => "filter-xpath",
let plugin_name = if content_type.to_lowercase().contains("json") {
"filter-jsonpath"
} else {
"filter-xpath"
};
let event = self