mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-09-04 17:07:26 +02:00
Don't block window creation on plugin runtime boot (#616)
This commit is contained in:
@@ -3,11 +3,10 @@ use std::collections::BTreeMap;
|
||||
use crate::PluginContextExt;
|
||||
use crate::error::Result;
|
||||
use KeyAndValueRef::{Ascii, Binary};
|
||||
use tauri::{Manager, Runtime, WebviewWindow};
|
||||
use tauri::{Runtime, WebviewWindow};
|
||||
use yaak_grpc::{KeyAndValueRef, MetadataMap};
|
||||
use yaak_models::models::GrpcRequest;
|
||||
use yaak_plugins::events::{CallHttpAuthenticationRequest, HttpHeader};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
|
||||
pub(crate) fn metadata_to_map(metadata: MetadataMap) -> BTreeMap<String, String> {
|
||||
let mut entries = BTreeMap::new();
|
||||
@@ -26,7 +25,7 @@ pub(crate) async fn build_metadata<R: Runtime>(
|
||||
request: &GrpcRequest,
|
||||
authentication_context_id: &str,
|
||||
) -> Result<BTreeMap<String, String>> {
|
||||
let plugin_manager = window.state::<PluginManager>();
|
||||
let plugin_manager = crate::plugins_ext::plugin_manager(window).await?;
|
||||
let mut metadata = BTreeMap::new();
|
||||
|
||||
// Add the rest of metadata
|
||||
|
||||
@@ -14,7 +14,6 @@ use yaak_http::manager::HttpConnectionManager;
|
||||
use yaak_models::models::{CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseState};
|
||||
use yaak_models::util::UpdateSource;
|
||||
use yaak_plugins::events::PluginContext;
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
|
||||
/// Context for managing response state during HTTP transactions.
|
||||
/// Handles both persisted responses (stored in DB) and ephemeral responses (in-memory only).
|
||||
@@ -149,7 +148,7 @@ async fn send_http_request_inner<R: Runtime>(
|
||||
response_ctx: &mut ResponseContext<R>,
|
||||
) -> Result<SentHttpRequest> {
|
||||
let app_handle = window.app_handle().clone();
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let connection_manager = app_handle.state::<HttpConnectionManager>();
|
||||
let environment_id = environment.map(|e| e.id);
|
||||
|
||||
@@ -7,7 +7,6 @@ use tauri::{Manager, Runtime, WebviewWindow};
|
||||
use yaak::import::{self, PlanImportDataParams};
|
||||
use yaak_api::{ApiClientKind, yaak_api_client};
|
||||
use yaak_models::util::{BatchUpsertResult, ImportDestination, ImportPlan};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
|
||||
pub(crate) async fn import_data<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
@@ -40,7 +39,7 @@ async fn plan_import_contents<R: Runtime>(
|
||||
contents: &str,
|
||||
destination: ImportDestination,
|
||||
) -> Result<ImportPlan> {
|
||||
let plugin_manager = window.state::<PluginManager>();
|
||||
let plugin_manager = crate::plugins_ext::plugin_manager(window).await?;
|
||||
let query_manager = window.db_manager();
|
||||
let plugin_context = window.plugin_context();
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ use yaak_plugins::events::{
|
||||
Color, ErrorResponse, FilterResponse, InternalEvent, InternalEventPayload, PluginContext,
|
||||
RenderPurpose, ShowToastRequest,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_rpc_schema::{AppMetaData, EphemeralHttpResponse};
|
||||
use yaak_sse::sse::ServerSentEvent;
|
||||
@@ -250,7 +249,7 @@ async fn cmd_grpc_reflect<R: Runtime>(
|
||||
let resolved_settings =
|
||||
app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?;
|
||||
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let req = render_grpc_request(
|
||||
&resolved_request,
|
||||
@@ -310,7 +309,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
let resolved_settings =
|
||||
app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?;
|
||||
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let request = render_grpc_request(
|
||||
&resolved_request,
|
||||
@@ -962,7 +961,6 @@ async fn cmd_format_graphql(text: &str) -> YaakResult<String> {
|
||||
|
||||
async fn cmd_http_response_body<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
response_id: &str,
|
||||
filter: Option<&str>,
|
||||
) -> YaakResult<FilterResponse> {
|
||||
@@ -977,7 +975,8 @@ async fn cmd_http_response_body<R: Runtime>(
|
||||
.ok_or(GenericError("Failed to find response body".to_string()))?;
|
||||
|
||||
match filter {
|
||||
Some(filter) if !filter.is_empty() => Ok(plugin_manager
|
||||
Some(filter) if !filter.is_empty() => Ok(plugins_ext::plugin_manager(&window)
|
||||
.await?
|
||||
.filter_data(&window.plugin_context(), filter, &body, content_type)
|
||||
.await?),
|
||||
_ => Ok(FilterResponse { content: body, error: None }),
|
||||
@@ -1450,7 +1449,10 @@ fn safe_uri(endpoint: &str) -> String {
|
||||
fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
let app_handle = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
||||
let plugin_manager = match plugins_ext::plugin_manager(&app_handle).await {
|
||||
Ok(pm) => pm,
|
||||
Err(_) => return, // The runtime failed to boot; there are no events
|
||||
};
|
||||
let (rx_id, mut rx) = plugin_manager.subscribe("app").await;
|
||||
|
||||
while let Some(event) = rx.recv().await {
|
||||
@@ -1491,9 +1493,13 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
}
|
||||
};
|
||||
|
||||
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
||||
if let Err(e) = plugin_manager.reply(&event, &ev).await {
|
||||
warn!("Failed to reply to plugin manager: {:?}", e)
|
||||
match plugins_ext::plugin_manager(&app_handle).await {
|
||||
Ok(pm) => {
|
||||
if let Err(e) = pm.reply(&event, &ev).await {
|
||||
warn!("Failed to reply to plugin manager: {:?}", e)
|
||||
}
|
||||
}
|
||||
Err(e) => warn!("Failed to get plugin manager for reply: {e:?}"),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ use yaak_plugins::events::{
|
||||
ShowToastRequest, TemplateRenderResponse, WindowInfoResponse, WindowNavigateEvent,
|
||||
WorkspaceInfo,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_handle::PluginHandle;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_tauri_utils::window::WorkspaceWindowTrait;
|
||||
@@ -205,7 +204,7 @@ async fn handle_host_plugin_request<R: Runtime>(
|
||||
req.grpc_request.folder_id.as_deref(),
|
||||
environment_id.as_deref(),
|
||||
)?;
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let cb = PluginTemplateCallback::new(
|
||||
plugin_manager,
|
||||
@@ -231,7 +230,7 @@ async fn handle_host_plugin_request<R: Runtime>(
|
||||
req.http_request.folder_id.as_deref(),
|
||||
environment_id.as_deref(),
|
||||
)?;
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let cb = PluginTemplateCallback::new(
|
||||
plugin_manager,
|
||||
@@ -267,7 +266,7 @@ async fn handle_host_plugin_request<R: Runtime>(
|
||||
folder_id.as_deref(),
|
||||
environment_id.as_deref(),
|
||||
)?;
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let cb = PluginTemplateCallback::new(
|
||||
plugin_manager,
|
||||
|
||||
@@ -31,10 +31,43 @@ use yaak_plugins::api::{
|
||||
use yaak_plugins::events::{Color, PluginContext, ShowToastRequest};
|
||||
use yaak_plugins::install::{delete_and_uninstall, download_and_install};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::error::Error::PluginErr;
|
||||
use yaak_plugins::plugin_meta::get_plugin_meta;
|
||||
|
||||
static EXITING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
// ============================================================================
|
||||
// Plugin Manager Handle
|
||||
// ============================================================================
|
||||
|
||||
/// The plugin runtime boots in the background so startup doesn't wait on it.
|
||||
/// This handle is the only way to reach the manager: [`PluginManagerHandle::get`]
|
||||
/// resolves once boot completes, so callers can never observe a
|
||||
/// partially-initialized runtime.
|
||||
#[derive(Clone)]
|
||||
pub struct PluginManagerHandle {
|
||||
rx: tokio::sync::watch::Receiver<Option<std::result::Result<PluginManager, String>>>,
|
||||
}
|
||||
|
||||
impl PluginManagerHandle {
|
||||
pub async fn get(&self) -> yaak_plugins::error::Result<PluginManager> {
|
||||
let mut rx = self.rx.clone();
|
||||
let result = rx
|
||||
.wait_for(|v| v.is_some())
|
||||
.await
|
||||
.map_err(|_| PluginErr("Plugin runtime boot task died".to_string()))?;
|
||||
result.clone().unwrap().map_err(PluginErr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the plugin runtime to finish booting and return the manager.
|
||||
pub async fn plugin_manager<R: Runtime>(
|
||||
manager: &impl Manager<R>,
|
||||
) -> yaak_plugins::error::Result<PluginManager> {
|
||||
let handle = manager.state::<PluginManagerHandle>().inner().clone();
|
||||
handle.get().await
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Plugin Updater
|
||||
// ============================================================================
|
||||
@@ -146,7 +179,7 @@ pub async fn cmd_plugins_install<R: Runtime>(
|
||||
name: &str,
|
||||
version: Option<String>,
|
||||
) -> Result<()> {
|
||||
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(plugin_manager(&window).await?);
|
||||
let app_version = window.app_handle().package_info().version.to_string();
|
||||
let http_client = yaak_api_client(ApiClientKind::App, &app_version)?;
|
||||
let query_manager = window.state::<yaak_models::query_manager::QueryManager>();
|
||||
@@ -167,6 +200,9 @@ pub async fn cmd_plugins_install_from_directory<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
directory: &str,
|
||||
) -> Result<Plugin> {
|
||||
// Resolve the manager before writing the row so startup's plugin snapshot
|
||||
// can't include it and boot it a second time
|
||||
let plugin_manager = Arc::new(plugin_manager(&window).await?);
|
||||
let plugin = window.db().upsert_plugin(
|
||||
&Plugin {
|
||||
directory: directory.into(),
|
||||
@@ -178,7 +214,6 @@ pub async fn cmd_plugins_install_from_directory<R: Runtime>(
|
||||
&UpdateSource::from_window_label(window.label()),
|
||||
)?;
|
||||
|
||||
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
|
||||
plugin_manager.add_plugin(&window.plugin_context(), &plugin).await?;
|
||||
|
||||
Ok(plugin)
|
||||
@@ -188,7 +223,7 @@ pub async fn cmd_plugins_uninstall<R: Runtime>(
|
||||
plugin_id: &str,
|
||||
window: WebviewWindow<R>,
|
||||
) -> Result<Plugin> {
|
||||
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(plugin_manager(&window).await?);
|
||||
let query_manager = window.state::<yaak_models::query_manager::QueryManager>();
|
||||
let plugin_context = window.plugin_context();
|
||||
Ok(delete_and_uninstall(plugin_manager, &query_manager, &plugin_context, plugin_id).await?)
|
||||
@@ -217,7 +252,7 @@ pub async fn cmd_plugins_update_all<R: Runtime>(
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(plugin_manager(&window).await?);
|
||||
let query_manager = window.state::<yaak_models::query_manager::QueryManager>();
|
||||
let plugin_context = window.plugin_context();
|
||||
|
||||
@@ -300,20 +335,38 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
let query_manager =
|
||||
app_handle.state::<yaak_models::query_manager::QueryManager>().inner().clone();
|
||||
|
||||
// Create plugin manager asynchronously
|
||||
// Boot the plugin runtime in the background so the window shows
|
||||
// immediately. Everything that needs plugins resolves the handle,
|
||||
// which waits for this task to finish.
|
||||
let (tx, rx) = tokio::sync::watch::channel(None);
|
||||
app_handle.manage(PluginManagerHandle { rx });
|
||||
let app_handle_clone = app_handle.clone();
|
||||
tauri::async_runtime::block_on(async move {
|
||||
let manager = PluginManager::new(
|
||||
vendored_plugin_dir,
|
||||
installed_plugin_dir,
|
||||
node_bin_path,
|
||||
plugin_runtime_main,
|
||||
&query_manager,
|
||||
&PluginContext::new_empty(),
|
||||
dev_mode,
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let result = tokio::time::timeout(
|
||||
Duration::from_secs(60),
|
||||
PluginManager::new(
|
||||
vendored_plugin_dir,
|
||||
installed_plugin_dir,
|
||||
node_bin_path,
|
||||
plugin_runtime_main,
|
||||
&query_manager,
|
||||
&PluginContext::new_empty(),
|
||||
dev_mode,
|
||||
),
|
||||
)
|
||||
.await
|
||||
.expect("Failed to start plugin runtime");
|
||||
.unwrap_or_else(|_| Err(yaak_plugins::error::Error::PluginErr(
|
||||
"Timed out starting the plugin runtime".to_string(),
|
||||
)));
|
||||
|
||||
let manager = match result {
|
||||
Ok(manager) => manager,
|
||||
Err(e) => {
|
||||
error!("Failed to start plugin runtime: {e:?}");
|
||||
let _ = tx.send(Some(Err(e.to_string())));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Surface unexpected runtime crashes to the user
|
||||
let mut crash_rx = manager.runtime_crash_rx();
|
||||
@@ -339,7 +392,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
}
|
||||
});
|
||||
|
||||
app_handle_clone.manage(manager);
|
||||
let _ = tx.send(Some(Ok(manager)));
|
||||
});
|
||||
|
||||
let plugin_updater = PluginUpdater::new();
|
||||
@@ -355,8 +408,14 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
api.prevent_exit();
|
||||
tauri::async_runtime::block_on(async move {
|
||||
info!("Exiting plugin runtime due to app exit");
|
||||
let manager: State<PluginManager> = app.state();
|
||||
manager.terminate().await;
|
||||
// Bound the wait in case the exit comes while boot is still
|
||||
// in flight
|
||||
let get_manager = plugin_manager(app);
|
||||
if let Ok(Ok(manager)) =
|
||||
tokio::time::timeout(Duration::from_secs(5), get_manager).await
|
||||
{
|
||||
manager.terminate().await;
|
||||
}
|
||||
app.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,10 +109,11 @@ impl<R: Runtime> Host for ClientCtx<R> {
|
||||
}
|
||||
|
||||
impl<R: Runtime> ClientCtx<R> {
|
||||
/// The plugin runtime this window talks to. Only the `PluginHost` impl
|
||||
/// below uses it; everything else goes through the trait.
|
||||
fn pm(&self) -> State<'_, PluginManager> {
|
||||
self.window.state::<PluginManager>()
|
||||
/// The plugin runtime this window talks to, once it finishes booting.
|
||||
/// Only the `PluginHost` impl below uses it; everything else goes through
|
||||
/// the trait.
|
||||
async fn pm(&self) -> yaak_plugins::error::Result<PluginManager> {
|
||||
crate::plugins_ext::plugin_manager(&self.window).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,35 +123,40 @@ impl<R: Runtime> ClientCtx<R> {
|
||||
/// providing them.
|
||||
impl<R: Runtime> PluginHost for ClientCtx<R> {
|
||||
async fn loaded_plugin_metadata(&self, directory: &str) -> Option<PluginMetadata> {
|
||||
let handle = self.pm().get_plugin_by_dir(directory).await?;
|
||||
let handle = self.pm().await.ok()?.get_plugin_by_dir(directory).await?;
|
||||
Some(handle.info())
|
||||
}
|
||||
|
||||
async fn take_plugin_init_errors(&self) -> Vec<(String, String)> {
|
||||
self.pm().take_init_errors().await
|
||||
match self.pm().await {
|
||||
Ok(pm) => pm.take_init_errors().await,
|
||||
Err(_) => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn resolve_plugins(&self, plugins: Vec<Plugin>) -> Vec<Plugin> {
|
||||
self.pm().resolve_plugins_for_runtime_from_db(plugins).await
|
||||
match self.pm().await {
|
||||
Ok(pm) => pm.resolve_plugins_for_runtime_from_db(plugins).await,
|
||||
Err(_) => plugins,
|
||||
}
|
||||
}
|
||||
|
||||
fn template_callback(&self, purpose: RenderPurpose) -> impl TemplateCallback {
|
||||
PluginTemplateCallback::new(
|
||||
Arc::new((*self.pm()).clone()),
|
||||
async fn template_callback(
|
||||
&self,
|
||||
purpose: RenderPurpose,
|
||||
) -> yaak_commands::Result<impl TemplateCallback> {
|
||||
Ok(PluginTemplateCallback::new(
|
||||
Arc::new(self.pm().await?),
|
||||
Arc::new(self.encryption_manager().clone()),
|
||||
&self.plugin_context(),
|
||||
purpose,
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
async fn template_function_summaries(
|
||||
&self,
|
||||
) -> yaak_commands::Result<Vec<GetTemplateFunctionSummaryResponse>> {
|
||||
Ok(self
|
||||
.window
|
||||
.state::<PluginManager>()
|
||||
.get_template_function_summaries(&self.plugin_context())
|
||||
.await?)
|
||||
Ok(self.pm().await?.get_template_function_summaries(&self.plugin_context()).await?)
|
||||
}
|
||||
|
||||
async fn template_function_config(
|
||||
@@ -160,81 +166,81 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
|
||||
model_id: &str,
|
||||
) -> yaak_commands::Result<GetTemplateFunctionConfigResponse> {
|
||||
Ok(self
|
||||
.window
|
||||
.state::<PluginManager>()
|
||||
.pm()
|
||||
.await?
|
||||
.get_template_function_config(&self.plugin_context(), function_name, values, model_id)
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn themes(&self) -> yaak_commands::Result<Vec<GetThemesResponse>> {
|
||||
Ok(self.pm().get_themes(&self.plugin_context()).await?)
|
||||
Ok(self.pm().await?.get_themes(&self.plugin_context()).await?)
|
||||
}
|
||||
|
||||
async fn http_request_actions(
|
||||
&self,
|
||||
) -> yaak_commands::Result<Vec<GetHttpRequestActionsResponse>> {
|
||||
Ok(self.pm().get_http_request_actions(&self.plugin_context()).await?)
|
||||
Ok(self.pm().await?.get_http_request_actions(&self.plugin_context()).await?)
|
||||
}
|
||||
|
||||
async fn websocket_request_actions(
|
||||
&self,
|
||||
) -> yaak_commands::Result<Vec<GetWebsocketRequestActionsResponse>> {
|
||||
Ok(self.pm().get_websocket_request_actions(&self.plugin_context()).await?)
|
||||
Ok(self.pm().await?.get_websocket_request_actions(&self.plugin_context()).await?)
|
||||
}
|
||||
|
||||
async fn grpc_request_actions(
|
||||
&self,
|
||||
) -> yaak_commands::Result<Vec<GetGrpcRequestActionsResponse>> {
|
||||
Ok(self.pm().get_grpc_request_actions(&self.plugin_context()).await?)
|
||||
Ok(self.pm().await?.get_grpc_request_actions(&self.plugin_context()).await?)
|
||||
}
|
||||
|
||||
async fn workspace_actions(&self) -> yaak_commands::Result<Vec<GetWorkspaceActionsResponse>> {
|
||||
Ok(self.pm().get_workspace_actions(&self.plugin_context()).await?)
|
||||
Ok(self.pm().await?.get_workspace_actions(&self.plugin_context()).await?)
|
||||
}
|
||||
|
||||
async fn folder_actions(&self) -> yaak_commands::Result<Vec<GetFolderActionsResponse>> {
|
||||
Ok(self.pm().get_folder_actions(&self.plugin_context()).await?)
|
||||
Ok(self.pm().await?.get_folder_actions(&self.plugin_context()).await?)
|
||||
}
|
||||
|
||||
async fn call_http_request_action(
|
||||
&self,
|
||||
req: CallHttpRequestActionRequest,
|
||||
) -> yaak_commands::Result<()> {
|
||||
Ok(self.pm().call_http_request_action(&self.plugin_context(), req).await?)
|
||||
Ok(self.pm().await?.call_http_request_action(&self.plugin_context(), req).await?)
|
||||
}
|
||||
|
||||
async fn call_grpc_request_action(
|
||||
&self,
|
||||
req: CallGrpcRequestActionRequest,
|
||||
) -> yaak_commands::Result<()> {
|
||||
Ok(self.pm().call_grpc_request_action(&self.plugin_context(), req).await?)
|
||||
Ok(self.pm().await?.call_grpc_request_action(&self.plugin_context(), req).await?)
|
||||
}
|
||||
|
||||
async fn call_websocket_request_action(
|
||||
&self,
|
||||
req: CallWebsocketRequestActionRequest,
|
||||
) -> yaak_commands::Result<()> {
|
||||
Ok(self.pm().call_websocket_request_action(&self.plugin_context(), req).await?)
|
||||
Ok(self.pm().await?.call_websocket_request_action(&self.plugin_context(), req).await?)
|
||||
}
|
||||
|
||||
async fn call_workspace_action(
|
||||
&self,
|
||||
req: CallWorkspaceActionRequest,
|
||||
) -> yaak_commands::Result<()> {
|
||||
Ok(self.pm().call_workspace_action(&self.plugin_context(), req).await?)
|
||||
Ok(self.pm().await?.call_workspace_action(&self.plugin_context(), req).await?)
|
||||
}
|
||||
|
||||
async fn call_folder_action(
|
||||
&self,
|
||||
req: CallFolderActionRequest,
|
||||
) -> yaak_commands::Result<()> {
|
||||
Ok(self.pm().call_folder_action(&self.plugin_context(), req).await?)
|
||||
Ok(self.pm().await?.call_folder_action(&self.plugin_context(), req).await?)
|
||||
}
|
||||
|
||||
async fn http_authentication_summaries(
|
||||
&self,
|
||||
) -> yaak_commands::Result<Vec<GetHttpAuthenticationSummaryResponse>> {
|
||||
let results = self.pm().get_http_authentication_summaries(&self.plugin_context()).await?;
|
||||
let results = self.pm().await?.get_http_authentication_summaries(&self.plugin_context()).await?;
|
||||
Ok(results.into_iter().map(|(_, a)| a).collect())
|
||||
}
|
||||
|
||||
@@ -246,6 +252,7 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
|
||||
) -> yaak_commands::Result<GetHttpAuthenticationConfigResponse> {
|
||||
Ok(self
|
||||
.pm()
|
||||
.await?
|
||||
.get_http_authentication_config(&self.plugin_context(), auth_name, values, model_id)
|
||||
.await?)
|
||||
}
|
||||
@@ -259,6 +266,7 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
|
||||
) -> yaak_commands::Result<()> {
|
||||
Ok(self
|
||||
.pm()
|
||||
.await?
|
||||
.call_http_authentication_action(
|
||||
&self.plugin_context(),
|
||||
auth_name,
|
||||
@@ -270,15 +278,18 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
|
||||
}
|
||||
|
||||
async fn import_data(&self, content: &str) -> yaak_commands::Result<ImportResponse> {
|
||||
Ok(self.pm().import_data(&self.plugin_context(), content).await?)
|
||||
Ok(self.pm().await?.import_data(&self.plugin_context(), content).await?)
|
||||
}
|
||||
|
||||
async fn reload_plugins(&self, plugins: Vec<Plugin>) -> Vec<(String, String)> {
|
||||
self.pm().initialize_all_plugins(plugins, &self.plugin_context()).await
|
||||
match self.pm().await {
|
||||
Ok(pm) => pm.initialize_all_plugins(plugins, &self.plugin_context()).await,
|
||||
Err(e) => vec![("*".to_string(), e.to_string())],
|
||||
}
|
||||
}
|
||||
|
||||
async fn encrypt_secure_template(&self, template: &str) -> yaak_commands::Result<String> {
|
||||
let plugin_manager = Arc::new((*self.pm()).clone());
|
||||
let plugin_manager = Arc::new(self.pm().await?);
|
||||
let encryption_manager = Arc::new(self.encryption_manager().clone());
|
||||
Ok(encrypt_secure_template_function(
|
||||
plugin_manager,
|
||||
@@ -422,7 +433,7 @@ async fn cmd_format_graphql<R: Runtime>(_ctx: ClientCtx<R>, req: CmdFormatGraphq
|
||||
}
|
||||
|
||||
async fn cmd_http_response_body<R: Runtime>(ctx: ClientCtx<R>, req: CmdHttpResponseBodyReq) -> Result<FilterResponse> {
|
||||
Ok(crate::cmd_http_response_body(ctx.window.clone(), ctx.window.app_handle().state::<PluginManager>(), &req.response_id, req.filter.as_deref()).await?)
|
||||
Ok(crate::cmd_http_response_body(ctx.window.clone(), &req.response_id, req.filter.as_deref()).await?)
|
||||
}
|
||||
|
||||
async fn cmd_http_response_body_path<R: Runtime>(ctx: ClientCtx<R>, req: CmdHttpResponseBodyPathReq) -> Result<Option<String>> {
|
||||
@@ -817,7 +828,7 @@ async fn cmd_ws_close<R: Runtime>(ctx: ClientCtx<R>, req: CmdWsCloseReq) -> Resu
|
||||
}
|
||||
|
||||
async fn cmd_ws_connect<R: Runtime>(ctx: ClientCtx<R>, req: CmdWsConnectReq) -> Result<WebsocketConnection> {
|
||||
Ok(crate::ws_ext::cmd_ws_connect(&req.request_id, req.environment_id.as_deref(), req.cookie_jar_id.as_deref(), ctx.window.app_handle().clone(), ctx.window.clone(), ctx.window.app_handle().state::<PluginManager>(), ctx.window.app_handle().state::<Mutex<WebsocketManager>>()).await?)
|
||||
Ok(crate::ws_ext::cmd_ws_connect(&req.request_id, req.environment_id.as_deref(), req.cookie_jar_id.as_deref(), ctx.window.app_handle().clone(), ctx.window.clone(), ctx.window.app_handle().state::<Mutex<WebsocketManager>>()).await?)
|
||||
}
|
||||
|
||||
async fn cmd_plugins_search<R: Runtime>(ctx: ClientCtx<R>, req: CmdPluginsSearchReq) -> Result<PluginSearchResponse> {
|
||||
|
||||
@@ -14,7 +14,6 @@ use tokio::task::block_in_place;
|
||||
use tokio::time::sleep;
|
||||
use ts_rs::TS;
|
||||
use yaak_models::util::generate_id;
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
|
||||
use url::Url;
|
||||
use yaak_api::get_system_proxy_url;
|
||||
@@ -98,8 +97,9 @@ impl YaakUpdater {
|
||||
block_in_place(|| {
|
||||
tauri::async_runtime::block_on(async move {
|
||||
info!("Shutting down plugin manager before update");
|
||||
let plugin_manager = w.state::<PluginManager>();
|
||||
plugin_manager.terminate().await;
|
||||
if let Ok(plugin_manager) = crate::plugins_ext::plugin_manager(&w).await {
|
||||
plugin_manager.terminate().await;
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -12,7 +12,6 @@ use yaak_api::{ApiClientKind, yaak_api_client};
|
||||
use yaak_models::util::generate_id;
|
||||
use yaak_plugins::events::{Color, ShowToastRequest};
|
||||
use yaak_plugins::install::download_and_install;
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
|
||||
pub(crate) async fn handle_deep_link<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
@@ -44,7 +43,7 @@ pub(crate) async fn handle_deep_link<R: Runtime>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(window).await?);
|
||||
let query_manager = app_handle.db_manager();
|
||||
let app_version = app_handle.package_info().version.to_string();
|
||||
let http_client = yaak_api_client(ApiClientKind::App, &app_version)?;
|
||||
|
||||
@@ -22,7 +22,6 @@ use yaak_models::models::{
|
||||
};
|
||||
use yaak_models::util::UpdateSource;
|
||||
use yaak_plugins::events::{CallHttpAuthenticationRequest, HttpHeader, RenderPurpose};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_templates::strip_json_comments::maybe_strip_json_comments;
|
||||
use yaak_templates::{RenderErrorBehavior, RenderOptions};
|
||||
@@ -77,7 +76,7 @@ async fn send_websocket_message<R: Runtime>(
|
||||
)?;
|
||||
let (resolved_request, _auth_context_id) =
|
||||
resolve_websocket_request(&window.db(), &unrendered_request)?;
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let request = render_websocket_request(
|
||||
&resolved_request,
|
||||
@@ -142,7 +141,6 @@ pub async fn cmd_ws_connect<R: Runtime>(
|
||||
cookie_jar_id: Option<&str>,
|
||||
app_handle: AppHandle<R>,
|
||||
window: WebviewWindow<R>,
|
||||
_plugin_manager: State<'_, PluginManager>,
|
||||
ws_manager: State<'_, Mutex<WebsocketManager>>,
|
||||
) -> Result<WebsocketConnection> {
|
||||
let unrendered_request = app_handle.db().get_websocket_request(request_id)?;
|
||||
@@ -156,7 +154,7 @@ pub async fn cmd_ws_connect<R: Runtime>(
|
||||
let settings = app_handle.db().get_settings();
|
||||
let (resolved_request, auth_context_id) =
|
||||
resolve_websocket_request(&window.db(), &unrendered_request)?;
|
||||
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
|
||||
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
|
||||
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
|
||||
let request = render_websocket_request(
|
||||
&resolved_request,
|
||||
|
||||
@@ -121,7 +121,12 @@ pub trait PluginHost: Host {
|
||||
/// a render — the variables come from the environment chain, which is an
|
||||
/// ordinary database read — so handing back the callback keeps the rest of
|
||||
/// rendering shared instead of pushing whole commands behind this trait.
|
||||
fn template_callback(&self, purpose: RenderPurpose) -> impl TemplateCallback;
|
||||
/// Async so hosts that finish booting their plugin runtime in the
|
||||
/// background can wait for it here.
|
||||
fn template_callback(
|
||||
&self,
|
||||
purpose: RenderPurpose,
|
||||
) -> impl Future<Output = crate::Result<impl TemplateCallback>>;
|
||||
|
||||
/// Every template function the installed plugins expose, for the
|
||||
/// autocomplete menu.
|
||||
|
||||
@@ -66,7 +66,7 @@ pub(crate) async fn render_form_values<H: PluginHost>(
|
||||
let environment_chain =
|
||||
host.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
|
||||
|
||||
let cb = host.template_callback(purpose);
|
||||
let cb = host.template_callback(purpose).await?;
|
||||
let rendered =
|
||||
render_json_value(serde_json::to_value(&values)?, environment_chain, &cb, options).await?;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ pub async fn cmd_render_template<H: PluginHost>(
|
||||
) -> Result<String> {
|
||||
let environment_chain =
|
||||
host.db().resolve_environments(&req.workspace_id, None, req.environment_id.as_deref())?;
|
||||
let cb = host.template_callback(req.purpose.unwrap_or(RenderPurpose::Preview));
|
||||
let cb = host.template_callback(req.purpose.unwrap_or(RenderPurpose::Preview)).await?;
|
||||
let options = RenderOptions {
|
||||
// A preview that throws would show the user an error where they expect
|
||||
// to see the value so far, so callers rendering *into the UI* ask for
|
||||
@@ -41,7 +41,7 @@ pub async fn cmd_template_tokens_to_string<H: PluginHost>(
|
||||
host: H,
|
||||
req: CmdTemplateTokensToStringReq,
|
||||
) -> Result<String> {
|
||||
let cb = host.template_callback(RenderPurpose::Preview);
|
||||
let cb = host.template_callback(RenderPurpose::Preview).await?;
|
||||
Ok(transform_args(req.tokens, &cb)?.to_string())
|
||||
}
|
||||
|
||||
|
||||
@@ -250,8 +250,11 @@ impl PluginHost for SingleThreadedHost {
|
||||
Err(yaak_commands::Error::Generic("no plugin runtime on this host".into()))
|
||||
}
|
||||
|
||||
fn template_callback(&self, _purpose: RenderPurpose) -> impl TemplateCallback {
|
||||
NoTemplateFunctions
|
||||
async fn template_callback(
|
||||
&self,
|
||||
_purpose: RenderPurpose,
|
||||
) -> yaak_commands::Result<impl TemplateCallback> {
|
||||
Ok(NoTemplateFunctions)
|
||||
}
|
||||
|
||||
async fn template_function_summaries(
|
||||
|
||||
@@ -187,24 +187,25 @@ impl PluginManager {
|
||||
}
|
||||
|
||||
let bundled_dirs = plugin_manager.list_bundled_plugin_dirs().await?;
|
||||
let db = query_manager.connect();
|
||||
for dir in &bundled_dirs {
|
||||
if db.get_plugin_by_directory(dir).is_none() {
|
||||
db.upsert_plugin(
|
||||
&Plugin {
|
||||
directory: dir.clone(),
|
||||
enabled: true,
|
||||
url: None,
|
||||
source: PluginSource::Bundled,
|
||||
..Default::default()
|
||||
},
|
||||
&UpdateSource::Background,
|
||||
)?;
|
||||
// Scope the db connection so the future stays Send across the await below
|
||||
let plugins = {
|
||||
let db = query_manager.connect();
|
||||
for dir in &bundled_dirs {
|
||||
if db.get_plugin_by_directory(dir).is_none() {
|
||||
db.upsert_plugin(
|
||||
&Plugin {
|
||||
directory: dir.clone(),
|
||||
enabled: true,
|
||||
url: None,
|
||||
source: PluginSource::Bundled,
|
||||
..Default::default()
|
||||
},
|
||||
&UpdateSource::Background,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let plugins = db.list_plugins()?;
|
||||
drop(db);
|
||||
db.list_plugins()?
|
||||
};
|
||||
|
||||
let init_errors = plugin_manager.initialize_all_plugins(plugins, plugin_context).await;
|
||||
if !init_errors.is_empty() {
|
||||
|
||||
Reference in New Issue
Block a user