Plugin execution context (#119)

This commit is contained in:
Gregory Schier
2024-09-30 17:45:51 -07:00
committed by GitHub
parent 917adcfb2e
commit 9915c57817
29 changed files with 637 additions and 373 deletions

View File

@@ -27,6 +27,7 @@ use yaak_models::models::{
Cookie, CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseHeader, HttpUrlParameter,
};
use yaak_models::queries::{get_workspace, update_response_if_id, upsert_cookie_jar};
use yaak_plugin_runtime::events::{RenderPurpose, WindowContext};
pub async fn send_http_request<R: Runtime>(
window: &WebviewWindow<R>,
@@ -39,8 +40,11 @@ pub async fn send_http_request<R: Runtime>(
let workspace = get_workspace(window, &request.workspace_id)
.await
.expect("Failed to get Workspace");
let cb = &*window.app_handle().state::<PluginTemplateCallback>();
let cb = cb.for_send();
let cb = PluginTemplateCallback::new(
window.app_handle(),
&WindowContext::from_window(window),
RenderPurpose::Send,
);
let rendered_request =
render_http_request(&request, &workspace, environment.as_ref(), &cb).await;

View File

@@ -16,6 +16,7 @@ use chrono::Utc;
use fern::colors::ColoredLevelConfig;
use log::{debug, error, info, warn};
use rand::random;
use regex::Regex;
use serde_json::{json, Value};
#[cfg(target_os = "macos")]
use tauri::TitleBarStyle;
@@ -36,7 +37,7 @@ use crate::export_resources::{get_workspace_export_resources, WorkspaceExportRes
use crate::grpc::metadata_to_map;
use crate::http_request::send_http_request;
use crate::notifications::YaakNotifier;
use crate::render::{render_grpc_request, render_http_request, render_template};
use crate::render::{render_grpc_request, render_json_value, render_template};
use crate::template_callback::PluginTemplateCallback;
use crate::updates::{UpdateMode, YaakUpdater};
use crate::window_menu::app_menu;
@@ -60,8 +61,8 @@ use yaak_models::queries::{
use yaak_plugin_runtime::events::{
BootResponse, CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, Icon,
InternalEvent, InternalEventPayload, RenderHttpRequestResponse, SendHttpRequestResponse,
ShowToastRequest,
InternalEvent, InternalEventPayload, RenderPurpose, SendHttpRequestResponse, ShowToastRequest,
TemplateRenderResponse, WindowContext,
};
use yaak_plugin_runtime::plugin_handle::PluginHandle;
use yaak_templates::{Parser, Tokens};
@@ -121,8 +122,9 @@ async fn cmd_template_tokens_to_string(tokens: Tokens) -> Result<String, String>
}
#[tauri::command]
async fn cmd_render_template(
window: WebviewWindow,
async fn cmd_render_template<R: Runtime>(
window: WebviewWindow<R>,
app_handle: AppHandle<R>,
template: &str,
workspace_id: &str,
environment_id: Option<&str>,
@@ -139,18 +141,22 @@ async fn cmd_render_template(
.await
.map_err(|e| e.to_string())?;
let rendered = render_template(
window.app_handle(),
template,
&workspace,
environment.as_ref(),
&PluginTemplateCallback::new(
&app_handle,
&WindowContext::from_window(&window),
RenderPurpose::Preview,
),
)
.await;
Ok(rendered)
}
#[tauri::command]
async fn cmd_dismiss_notification(
window: WebviewWindow,
async fn cmd_dismiss_notification<R: Runtime>(
window: WebviewWindow<R>,
notification_id: &str,
yaak_notifier: State<'_, Mutex<YaakNotifier>>,
) -> Result<(), String> {
@@ -162,10 +168,10 @@ async fn cmd_dismiss_notification(
}
#[tauri::command]
async fn cmd_grpc_reflect(
async fn cmd_grpc_reflect<R: Runtime>(
request_id: &str,
proto_files: Vec<String>,
window: WebviewWindow,
window: WebviewWindow<R>,
grpc_handle: State<'_, Mutex<GrpcHandle>>,
) -> Result<Vec<ServiceDefinition>, String> {
let req = get_grpc_request(&window, request_id)
@@ -189,11 +195,11 @@ async fn cmd_grpc_reflect(
}
#[tauri::command]
async fn cmd_grpc_go(
async fn cmd_grpc_go<R: Runtime>(
request_id: &str,
environment_id: Option<&str>,
proto_files: Vec<String>,
window: WebviewWindow,
window: WebviewWindow<R>,
grpc_handle: State<'_, Mutex<GrpcHandle>>,
) -> Result<String, String> {
let environment = match environment_id {
@@ -210,8 +216,17 @@ async fn cmd_grpc_go(
let workspace = get_workspace(&window, &req.workspace_id)
.await
.map_err(|e| e.to_string())?;
let req =
render_grpc_request(window.app_handle(), &req, &workspace, environment.as_ref()).await;
let req = render_grpc_request(
&req,
&workspace,
environment.as_ref(),
&PluginTemplateCallback::new(
window.app_handle(),
&WindowContext::from_window(&window),
RenderPurpose::Send,
),
)
.await;
let mut metadata = BTreeMap::new();
// Add the rest of metadata
@@ -345,7 +360,7 @@ async fn cmd_grpc_go(
move |ev: tauri::Event| {
if *cancelled_rx.borrow() {
// Stream is cancelled
// Stream is canceled
return;
}
@@ -750,13 +765,13 @@ async fn cmd_send_ephemeral_request(
}
#[tauri::command]
async fn cmd_filter_response(
w: WebviewWindow,
async fn cmd_filter_response<R: Runtime>(
window: WebviewWindow<R>,
response_id: &str,
plugin_manager: State<'_, PluginManager>,
filter: &str,
) -> Result<FilterResponse, String> {
let response = get_http_response(&w, response_id)
let response = get_http_response(&window, response_id)
.await
.expect("Failed to get http response");
@@ -776,14 +791,14 @@ async fn cmd_filter_response(
// TODO: Have plugins register their own content type (regex?)
plugin_manager
.filter_data(filter, &body, &content_type)
.filter_data(&window, filter, &body, &content_type)
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_import_data(
w: WebviewWindow,
async fn cmd_import_data<R: Runtime>(
window: WebviewWindow<R>,
plugin_manager: State<'_, PluginManager>,
file_path: &str,
) -> Result<WorkspaceExportResources, String> {
@@ -791,7 +806,7 @@ async fn cmd_import_data(
read_to_string(file_path).unwrap_or_else(|_| panic!("Unable to read file {}", file_path));
let file_contents = file.as_str();
let (import_result, plugin_name) = plugin_manager
.import_data(file_contents)
.import_data(&window, file_contents)
.await
.map_err(|e| e.to_string())?;
@@ -828,7 +843,9 @@ async fn cmd_import_data(
for mut v in resources.workspaces {
v.id = maybe_gen_id(v.id.as_str(), ModelType::TypeWorkspace, &mut id_map);
let x = upsert_workspace(&w, v).await.map_err(|e| e.to_string())?;
let x = upsert_workspace(&window, v)
.await
.map_err(|e| e.to_string())?;
imported_resources.workspaces.push(x.clone());
}
info!(
@@ -843,7 +860,9 @@ async fn cmd_import_data(
ModelType::TypeWorkspace,
&mut id_map,
);
let x = upsert_environment(&w, v).await.map_err(|e| e.to_string())?;
let x = upsert_environment(&window, v)
.await
.map_err(|e| e.to_string())?;
imported_resources.environments.push(x.clone());
}
info!(
@@ -875,7 +894,7 @@ async fn cmd_import_data(
if let Some(_) = imported_resources.folders.iter().find(|f| f.id == v.id) {
continue;
}
let x = upsert_folder(&w, v).await.map_err(|e| e.to_string())?;
let x = upsert_folder(&window, v).await.map_err(|e| e.to_string())?;
imported_resources.folders.push(x.clone());
}
}
@@ -889,7 +908,7 @@ async fn cmd_import_data(
&mut id_map,
);
v.folder_id = maybe_gen_id_opt(v.folder_id, ModelType::TypeFolder, &mut id_map);
let x = upsert_http_request(&w, v)
let x = upsert_http_request(&window, v)
.await
.map_err(|e| e.to_string())?;
imported_resources.http_requests.push(x.clone());
@@ -907,7 +926,7 @@ async fn cmd_import_data(
&mut id_map,
);
v.folder_id = maybe_gen_id_opt(v.folder_id, ModelType::TypeFolder, &mut id_map);
let x = upsert_grpc_request(&w, &v)
let x = upsert_grpc_request(&window, &v)
.await
.map_err(|e| e.to_string())?;
imported_resources.grpc_requests.push(x.clone());
@@ -918,7 +937,7 @@ async fn cmd_import_data(
);
analytics::track_event(
&w,
&window,
AnalyticsResource::App,
AnalyticsAction::Import,
Some(json!({ "plugin": plugin_name })),
@@ -929,52 +948,55 @@ async fn cmd_import_data(
}
#[tauri::command]
async fn cmd_http_request_actions(
async fn cmd_http_request_actions<R: Runtime>(
window: WebviewWindow<R>,
plugin_manager: State<'_, PluginManager>,
) -> Result<Vec<GetHttpRequestActionsResponse>, String> {
plugin_manager
.get_http_request_actions()
.get_http_request_actions(&window)
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_template_functions(
async fn cmd_template_functions<R: Runtime>(
window: WebviewWindow<R>,
plugin_manager: State<'_, PluginManager>,
) -> Result<Vec<GetTemplateFunctionsResponse>, String> {
plugin_manager
.get_template_functions()
.get_template_functions(&window)
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_call_http_request_action(
async fn cmd_call_http_request_action<R: Runtime>(
window: WebviewWindow<R>,
req: CallHttpRequestActionRequest,
plugin_manager: State<'_, PluginManager>,
) -> Result<(), String> {
plugin_manager
.call_http_request_action(req)
.call_http_request_action(&window, req)
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_curl_to_request(
async fn cmd_curl_to_request<R: Runtime>(
window: WebviewWindow<R>,
command: &str,
plugin_manager: State<'_, PluginManager>,
workspace_id: &str,
w: WebviewWindow,
) -> Result<HttpRequest, String> {
let (import_result, plugin_name) = {
plugin_manager
.import_data(command)
.import_data(&window, command)
.await
.map_err(|e| e.to_string())?
};
analytics::track_event(
&w,
&window,
AnalyticsResource::App,
AnalyticsAction::Import,
Some(json!({ "plugin": plugin_name })),
@@ -1176,19 +1198,19 @@ async fn cmd_create_workspace(name: &str, w: WebviewWindow) -> Result<Workspace,
}
#[tauri::command]
async fn cmd_install_plugin(
async fn cmd_install_plugin<R: Runtime>(
directory: &str,
url: Option<String>,
plugin_manager: State<'_, PluginManager>,
w: WebviewWindow,
window: WebviewWindow<R>,
) -> Result<Plugin, String> {
plugin_manager
.add_plugin_by_dir(&directory, true)
.add_plugin_by_dir(WindowContext::from_window(&window), &directory, true)
.await
.map_err(|e| e.to_string())?;
let plugin = upsert_plugin(
&w,
&window,
Plugin {
directory: directory.into(),
url,
@@ -1202,17 +1224,20 @@ async fn cmd_install_plugin(
}
#[tauri::command]
async fn cmd_uninstall_plugin(
async fn cmd_uninstall_plugin<R: Runtime>(
plugin_id: &str,
plugin_manager: State<'_, PluginManager>,
w: WebviewWindow,
window: WebviewWindow<R>,
) -> Result<Plugin, String> {
let plugin = delete_plugin(&w, plugin_id)
let plugin = delete_plugin(&window, plugin_id)
.await
.map_err(|e| e.to_string())?;
plugin_manager
.uninstall(plugin.directory.as_str())
.uninstall(
WindowContext::from_window(&window),
plugin.directory.as_str(),
)
.await
.map_err(|e| e.to_string())?;
@@ -1493,12 +1518,12 @@ async fn cmd_list_plugins(w: WebviewWindow) -> Result<Vec<Plugin>, String> {
}
#[tauri::command]
async fn cmd_reload_plugins(
app_handle: AppHandle,
async fn cmd_reload_plugins<R: Runtime>(
window: WebviewWindow<R>,
plugin_manager: State<'_, PluginManager>,
) -> Result<(), String> {
plugin_manager
.initialize_all_plugins(&app_handle)
.initialize_all_plugins(window.app_handle(), WindowContext::from_window(&window))
.await
.map_err(|e| e.to_string())?;
Ok(())
@@ -1828,10 +1853,6 @@ pub fn run() {
let grpc_handle = GrpcHandle::new(&app.app_handle());
app.manage(Mutex::new(grpc_handle));
// Plugin template callback
let plugin_cb = PluginTemplateCallback::new(app.app_handle().clone());
app.manage(plugin_cb);
monitor_plugin_events(&app.app_handle().clone());
Ok(())
@@ -2143,6 +2164,7 @@ async fn handle_plugin_event<R: Runtime>(
plugin_handle: &PluginHandle,
) {
// info!("Got event to app {}", event.id);
let window_context = event.window_context.to_owned();
let response_event: Option<InternalEventPayload> = match event.clone().payload {
InternalEventPayload::CopyTextRequest(req) => {
app_handle
@@ -2152,9 +2174,14 @@ async fn handle_plugin_event<R: Runtime>(
None
}
InternalEventPayload::ShowToastRequest(req) => {
app_handle
.emit("show_toast", req)
.expect("Failed to emit show_toast");
match window_context {
WindowContext::Label { label } => app_handle
.emit_to(label, "show_toast", req)
.expect("Failed to emit show_toast to window"),
_ => app_handle
.emit("show_toast", req)
.expect("Failed to emit show_toast"),
};
None
}
InternalEventPayload::FindHttpResponsesRequest(req) => {
@@ -2175,33 +2202,24 @@ async fn handle_plugin_event<R: Runtime>(
GetHttpRequestByIdResponse { http_request },
))
}
InternalEventPayload::RenderHttpRequestRequest(req) => {
let window = get_focused_window_no_lock(app_handle).expect("No focused window");
let workspace = get_workspace(app_handle, req.http_request.workspace_id.as_str())
.await
.expect("Failed to get workspace for request");
InternalEventPayload::TemplateRenderRequest(req) => {
let window = get_window_from_window_context(app_handle, &window_context)
.expect("Failed to find window");
let url = window.url().unwrap();
let mut query_pairs = url.query_pairs();
let environment_id = query_pairs
.find(|(k, _v)| k == "environment_id")
.map(|(_k, v)| v.to_string());
let environment = match environment_id {
None => None,
Some(id) => get_environment(&window, id.as_str()).await.ok(),
};
let cb = &*app_handle.state::<PluginTemplateCallback>();
let rendered_http_request =
render_http_request(&req.http_request, &workspace, environment.as_ref(), cb).await;
Some(InternalEventPayload::RenderHttpRequestResponse(
RenderHttpRequestResponse {
http_request: rendered_http_request,
},
let workspace = workspace_from_window(&window)
.await
.expect("Failed to get workspace_id from window URL");
let environment = environment_from_window(&window).await;
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
let data = render_json_value(req.data, &workspace, environment.as_ref(), &cb).await;
Some(InternalEventPayload::TemplateRenderResponse(
TemplateRenderResponse { data },
))
}
InternalEventPayload::ReloadResponse => {
let window = get_focused_window_no_lock(app_handle).expect("No focused window");
let plugins = list_plugins(&window).await.unwrap();
let window = get_window_from_window_context(app_handle, &window_context)
.expect("Failed to find window");
let plugins = list_plugins(app_handle).await.unwrap();
for plugin in plugins {
if plugin.directory != plugin_handle.dir {
continue;
@@ -2214,6 +2232,7 @@ async fn handle_plugin_event<R: Runtime>(
upsert_plugin(&window, new_plugin).await.unwrap();
}
let toast_event = plugin_handle.build_event_to_send(
WindowContext::from_window(&window),
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
message: format!("Reloaded plugin {}", plugin_handle.dir),
icon: Some(Icon::Info),
@@ -2225,32 +2244,17 @@ async fn handle_plugin_event<R: Runtime>(
None
}
InternalEventPayload::SendHttpRequestRequest(req) => {
let w = get_focused_window_no_lock(app_handle).expect("No focused window");
let url = w.url().unwrap();
let mut query_pairs = url.query_pairs();
let window = get_window_from_window_context(app_handle, &window_context)
.expect("Failed to find window");
let cookie_jar = cookie_jar_from_window(&window).await;
let environment = environment_from_window(&window).await;
let cookie_jar_id = query_pairs
.find(|(k, _v)| k == "cookie_jar_id")
.map(|(_k, v)| v.to_string());
let cookie_jar = match cookie_jar_id {
None => None,
Some(id) => get_cookie_jar(app_handle, id.as_str()).await.ok(),
};
let environment_id = query_pairs
.find(|(k, _v)| k == "environment_id")
.map(|(_k, v)| v.to_string());
let environment = match environment_id {
None => None,
Some(id) => get_environment(app_handle, id.as_str()).await.ok(),
};
let resp = create_default_http_response(&w, req.http_request.id.as_str())
let resp = create_default_http_response(&window, req.http_request.id.as_str())
.await
.unwrap();
let result = send_http_request(
&w,
&window,
&req.http_request,
&resp,
environment,
@@ -2279,29 +2283,66 @@ async fn handle_plugin_event<R: Runtime>(
}
}
// app_handle.get_focused_window locks, so this one is a non-locking version, safe for use in async context
fn get_focused_window_no_lock<R: Runtime>(app_handle: &AppHandle<R>) -> Option<WebviewWindow<R>> {
// TODO: Getting the focused window doesn't seem to work on Windows, so
// we'll need to pass the window label into plugin events instead.
let main_windows = app_handle
.webview_windows()
.iter()
.filter_map(|(_, w)| {
if w.label().starts_with(MAIN_WINDOW_PREFIX) {
Some(w.to_owned())
} else {
None
}
})
.collect::<Vec<WebviewWindow<R>>>();
fn get_window_from_window_context<R: Runtime>(
app_handle: &AppHandle<R>,
window_context: &WindowContext,
) -> Option<WebviewWindow<R>> {
let label = match window_context {
WindowContext::None => return None,
WindowContext::Label { label } => label,
};
if main_windows.len() == 1 {
return main_windows.iter().next().map(|w| w.clone());
}
main_windows
.iter()
.cloned()
.find(|w| w.is_focused().unwrap_or(false))
.map(|w| w.clone())
app_handle.webview_windows().iter().find_map(|(_, w)| {
if w.label() == label {
Some(w.to_owned())
} else {
None
}
})
}
fn workspace_id_from_window<R: Runtime>(window: &WebviewWindow<R>) -> Option<String> {
let url = window.url().unwrap();
let re = Regex::new(r"/workspaces/(?<wid>\w+)").unwrap();
match re.captures(url.as_str()) {
None => None,
Some(captures) => captures.name("wid").map(|c| c.as_str().to_string()),
}
}
async fn workspace_from_window<R: Runtime>(window: &WebviewWindow<R>) -> Option<Workspace> {
match workspace_id_from_window(&window) {
None => None,
Some(id) => get_workspace(window, id.as_str()).await.ok(),
}
}
fn environment_id_from_window<R: Runtime>(window: &WebviewWindow<R>) -> Option<String> {
let url = window.url().unwrap();
let mut query_pairs = url.query_pairs();
query_pairs
.find(|(k, _v)| k == "environment_id")
.map(|(_k, v)| v.to_string())
}
async fn environment_from_window<R: Runtime>(window: &WebviewWindow<R>) -> Option<Environment> {
match environment_id_from_window(&window) {
None => None,
Some(id) => get_environment(window, id.as_str()).await.ok(),
}
}
fn cookie_jar_id_from_window<R: Runtime>(window: &WebviewWindow<R>) -> Option<String> {
let url = window.url().unwrap();
let mut query_pairs = url.query_pairs();
query_pairs
.find(|(k, _v)| k == "cookie_jar_id")
.map(|(_k, v)| v.to_string())
}
async fn cookie_jar_from_window<R: Runtime>(window: &WebviewWindow<R>) -> Option<CookieJar> {
match cookie_jar_id_from_window(&window) {
None => None,
Some(id) => get_cookie_jar(window, id.as_str()).await.ok(),
}
}

View File

@@ -51,7 +51,7 @@ impl YaakNotifier {
Ok(())
}
pub async fn check<R: Runtime>(&mut self, w: &WebviewWindow<R>) -> Result<(), String> {
pub async fn check<R: Runtime>(&mut self, window: &WebviewWindow<R>) -> Result<(), String> {
let ignore_check = self.last_check.elapsed().unwrap().as_secs() < MAX_UPDATE_CHECK_SECONDS;
if ignore_check {
@@ -60,8 +60,8 @@ impl YaakNotifier {
self.last_check = SystemTime::now();
let num_launches = get_num_launches(w).await;
let info = w.app_handle().package_info().clone();
let num_launches = get_num_launches(window).await;
let info = window.app_handle().package_info().clone();
let req = reqwest::Client::default()
.request(Method::GET, "https://notify.yaak.app/notifications")
.query(&[
@@ -80,14 +80,14 @@ impl YaakNotifier {
.map_err(|e| e.to_string())?;
let age = notification.timestamp.signed_duration_since(Utc::now());
let seen = get_kv(w).await?;
let seen = get_kv(window).await?;
if seen.contains(&notification.id) || (age > Duration::days(2)) {
debug!("Already seen notification {}", notification.id);
return Ok(());
}
debug!("Got notification {:?}", notification);
let _ = w.emit("notification", notification.clone());
let _ = window.emit_to(window.label(), "notification", notification.clone());
Ok(())
}

View File

@@ -1,31 +1,38 @@
use crate::template_callback::PluginTemplateCallback;
use serde_json::{json, Map, Value};
use std::collections::{BTreeMap, HashMap};
use tauri::{AppHandle, Manager, Runtime};
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>(
app_handle: &AppHandle<R>,
pub async fn render_template<T: TemplateCallback>(
template: &str,
w: &Workspace,
e: Option<&Environment>,
cb: &T,
) -> String {
let cb = &*app_handle.state::<PluginTemplateCallback>();
let vars = &make_vars_hashmap(w, e);
render(template, vars, cb).await
}
pub async fn render_grpc_request<R: Runtime>(
app_handle: &AppHandle<R>,
pub async fn render_json_value<T: TemplateCallback>(
value: Value,
w: &Workspace,
e: Option<&Environment>,
cb: &T,
) -> Value {
let vars = &make_vars_hashmap(w, e);
render_json_value_raw(value, vars, cb).await
}
pub async fn render_grpc_request<T: TemplateCallback>(
r: &GrpcRequest,
w: &Workspace,
e: Option<&Environment>,
cb: &T,
) -> GrpcRequest {
let cb = &*app_handle.state::<PluginTemplateCallback>();
let vars = &make_vars_hashmap(w, e);
let mut metadata = Vec::new();
@@ -39,7 +46,7 @@ pub async fn render_grpc_request<R: Runtime>(
let mut authentication = BTreeMap::new();
for (k, v) in r.authentication.clone() {
authentication.insert(k, render_json_value(v, vars, cb).await);
authentication.insert(k, render_json_value_raw(v, vars, cb).await);
}
let url = render(r.url.as_str(), vars, cb).await;
@@ -80,12 +87,12 @@ pub async fn render_http_request(
let mut body = BTreeMap::new();
for (k, v) in r.body.clone() {
body.insert(k, render_json_value(v, vars, cb).await);
body.insert(k, render_json_value_raw(v, vars, cb).await);
}
let mut authentication = BTreeMap::new();
for (k, v) in r.authentication.clone() {
authentication.insert(k, render_json_value(v, vars, cb).await);
authentication.insert(k, render_json_value_raw(v, vars, cb).await);
}
let url = render(r.url.clone().as_str(), vars, cb).await;
@@ -138,7 +145,7 @@ fn add_variable_to_map(
map
}
pub async fn render_json_value<T: TemplateCallback>(
async fn render_json_value_raw<T: TemplateCallback>(
v: Value,
vars: &HashMap<String, String>,
cb: &T,
@@ -148,7 +155,7 @@ pub async fn render_json_value<T: TemplateCallback>(
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)
new_a.push(Box::pin(render_json_value_raw(v, vars, cb)).await)
}
json!(new_a)
}
@@ -156,7 +163,7 @@ pub async fn render_json_value<T: TemplateCallback>(
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;
let value = Box::pin(render_json_value_raw(v, vars, cb)).await;
new_o.insert(key, value);
}
json!(new_o)
@@ -189,7 +196,7 @@ mod tests {
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
let result = super::render_json_value_raw(v, &vars, &EmptyCB {}).await;
assert_eq!(result, json!("aaa"))
}
@@ -199,7 +206,7 @@ mod tests {
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
let result = super::render_json_value_raw(v, &vars, &EmptyCB {}).await;
assert_eq!(result, json!(["aaa", "aaa"]))
}
@@ -209,7 +216,7 @@ mod tests {
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
let result = super::render_json_value_raw(v, &vars, &EmptyCB {}).await;
assert_eq!(result, json!({"aaa": "aaa"}))
}
@@ -226,7 +233,7 @@ mod tests {
let mut vars = HashMap::new();
vars.insert("a".to_string(), "aaa".to_string());
let result = super::render_json_value(v, &vars, &EmptyCB {}).await;
let result = super::render_json_value_raw(v, &vars, &EmptyCB {}).await;
assert_eq!(
result,
json!([

View File

@@ -1,32 +1,34 @@
use std::collections::HashMap;
use tauri::{AppHandle, Manager};
use yaak_plugin_runtime::events::{RenderPurpose, TemplateFunctionArg};
use tauri::{AppHandle, Manager, Runtime};
use yaak_plugin_runtime::events::{RenderPurpose, TemplateFunctionArg, WindowContext};
use yaak_plugin_runtime::manager::PluginManager;
use yaak_templates::TemplateCallback;
#[derive(Clone)]
pub struct PluginTemplateCallback {
app_handle: AppHandle,
purpose: RenderPurpose,
plugin_manager: PluginManager,
window_context: WindowContext,
render_purpose: RenderPurpose,
}
impl PluginTemplateCallback {
pub fn new(app_handle: AppHandle) -> PluginTemplateCallback {
pub fn new<R: Runtime>(
app_handle: &AppHandle<R>,
window_context: &WindowContext,
render_purpose: RenderPurpose,
) -> PluginTemplateCallback {
let plugin_manager = &*app_handle.state::<PluginManager>();
PluginTemplateCallback {
app_handle,
purpose: RenderPurpose::Preview,
plugin_manager: plugin_manager.to_owned(),
window_context: window_context.to_owned(),
render_purpose,
}
}
pub fn for_send(&self) -> PluginTemplateCallback {
let mut v = self.clone();
v.purpose = RenderPurpose::Send;
v
}
}
impl TemplateCallback for PluginTemplateCallback {
async fn run(&self, fn_name: &str, args: HashMap<String, String>) -> Result<String, String> {
let window_context = self.window_context.to_owned();
// The beta named the function `Response` but was changed in stable.
// Keep this here for a while because there's no easy way to migrate
let fn_name = if fn_name == "Response" {
@@ -35,9 +37,9 @@ impl TemplateCallback for PluginTemplateCallback {
fn_name
};
let plugin_manager = self.app_handle.state::<PluginManager>();
let function = plugin_manager
.get_template_functions()
let function = self
.plugin_manager
.get_template_functions_with_context(window_context.to_owned())
.await
.map_err(|e| e.to_string())?
.iter()
@@ -60,8 +62,14 @@ impl TemplateCallback for PluginTemplateCallback {
}
}
let resp = plugin_manager
.call_template_function(fn_name, args_with_defaults, self.purpose.clone())
let resp = self
.plugin_manager
.call_template_function(
window_context,
fn_name,
args_with_defaults,
self.render_purpose.to_owned(),
)
.await
.map_err(|e| e.to_string())?;
Ok(resp.unwrap_or_default())