//! What a command needs from whatever is running it. //! //! A command handler is invoked on behalf of one client (a desktop window today) //! and needs a handful of things from its surroundings: the shared engine //! managers, who the client is, what the client is looking at, and a little //! about the app. `Host` is that handful and nothing more. The desktop //! implements it over a `WebviewWindow`; a server would implement it over a //! connection. Handlers are generic over it, so the same handler body runs //! under either without knowing which. //! //! The surface grows only when a handler being moved here needs something new, //! and stays as narrow as those handlers allow. What is deliberately *not* here //! is anything only a desktop can do — open a native window, run the updater, //! show a native dialog — those handlers stay with the desktop. use std::collections::HashMap; use std::future::Future; use yaak_core::WorkspaceContext; use yaak_crypto::manager::EncryptionManager; use yaak_models::blob_manager::{BlobContext, BlobManager}; use yaak_models::client_db::ClientDb; use yaak_models::models::Plugin; use yaak_models::query_manager::QueryManager; use yaak_models::util::UpdateSource; use yaak_plugins::events::{ CallFolderActionRequest, CallGrpcRequestActionRequest, CallHttpRequestActionRequest, CallWebsocketRequestActionRequest, CallWorkspaceActionRequest, GetFolderActionsResponse, GetGrpcRequestActionsResponse, GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse, GetHttpRequestActionsResponse, GetTemplateFunctionConfigResponse, GetTemplateFunctionSummaryResponse, GetThemesResponse, GetWebsocketRequestActionsResponse, GetWorkspaceActionsResponse, ImportResponse, JsonPrimitive, PluginContext, RenderPurpose, }; use yaak_plugins::plugin_meta::PluginMetadata; use yaak_templates::TemplateCallback; /// Only `Clone` is required here. `Send`/`Sync`/`'static` are deliberately /// *not*: a browser host is single-threaded and its connection pool is an /// `Rc` — `rusqlite::Connection` is not `Sync` to begin with — so a /// thread-safety bound on the trait would lock that host out of implementing it /// at all. The router needs those bounds and states them itself, which is where /// they belong: they are a property of a particular transport, not of a command. pub trait Host: Clone { /// Stable identity of the client this call is for. On the desktop this is /// the window label. It rides on every model write so the client that made /// a change can tell its own echo from everyone else's. fn client_id(&self) -> &str; /// What the client is currently looking at: workspace, environment, cookie /// jar, request. Read at call time, since the client can navigate between /// calls (and during one). fn session(&self) -> WorkspaceContext; /// The app version, as reported to the Yaak API and stamped on exports. fn app_version(&self) -> String; fn query_manager(&self) -> &QueryManager; fn blob_manager(&self) -> &BlobManager; fn encryption_manager(&self) -> &EncryptionManager; // -- Conveniences derived from the above; hosts do not override these -- fn update_source(&self) -> UpdateSource { UpdateSource::from_window_label(self.client_id()) } fn plugin_context(&self) -> PluginContext { PluginContext::new(Some(self.client_id().to_string()), self.session().workspace_id) } fn db(&self) -> ClientDb<'_> { self.query_manager().connect() } fn blobs(&self) -> BlobContext { self.blob_manager().connect() } } /// A host that can also reach plugins. /// /// Separate from [`Host`] so that a command which only touches the database /// never demands a plugin runtime it does not call: a host with no plugins /// still serves those, and only handlers bounded on `PluginHost` are closed to /// it. /// /// These are *operations*, not a handle. Handing back a `&PluginManager` would /// have been shorter, but that type is specifically "spawn a Node sidecar and /// talk to it over a socket", and a browser host runs plugins in a Worker it /// reaches by message — it can answer any of the questions below and can never /// produce that type. Naming the questions instead of the answerer is what lets /// both hosts exist. /// /// Same rule as [`Host`]: this grows only when a migrated handler needs /// something new, and stays as narrow as those handlers allow. Today it is the /// four things batch 1 asks for. /// /// The types crossing this boundary still come from `yaak-plugins` — fine on /// the desktop, and once its plain data types are split out from its runtime /// that becomes an import-path change here rather than an interface one. pub trait PluginHost: Host { /// What the running plugin runtime knows about the plugin installed in /// `directory`, or `None` if it has not loaded one from there. Callers fall /// back to reading the plugin's manifest off disk. fn loaded_plugin_metadata( &self, directory: &str, ) -> impl Future>; /// Failures from plugin initialization, drained — reporting them clears /// them, so a caller that drops these has lost them. fn take_plugin_init_errors(&self) -> impl Future>; /// The plugin rows as the runtime sees them: the database says what is /// installed, the runtime knows which are bundled and what version actually /// loaded. A host without a runtime can return them untouched. fn resolve_plugins(&self, plugins: Vec) -> impl Future>; /// The template functions this host can run, as a callback the renderer /// drives. This is the *only* thing the plugin runtime uniquely provides to /// 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; /// Every template function the installed plugins expose, for the /// autocomplete menu. fn template_function_summaries( &self, ) -> impl Future>>; /// The form a template function wants to show for the given values. fn template_function_config( &self, function_name: &str, values: HashMap, model_id: &str, ) -> impl Future>; /// Themes contributed by plugins. fn themes(&self) -> impl Future>>; // -- Actions plugins contribute to the UI -- fn http_request_actions( &self, ) -> impl Future>>; fn websocket_request_actions( &self, ) -> impl Future>>; fn grpc_request_actions( &self, ) -> impl Future>>; fn workspace_actions( &self, ) -> impl Future>>; fn folder_actions(&self) -> impl Future>>; /// Running an action. The request in each of these has already been /// re-read and had its inheritance resolved by the handler; a host must /// pass it through untouched. fn call_http_request_action( &self, req: CallHttpRequestActionRequest, ) -> impl Future>; fn call_grpc_request_action( &self, req: CallGrpcRequestActionRequest, ) -> impl Future>; fn call_websocket_request_action( &self, req: CallWebsocketRequestActionRequest, ) -> impl Future>; fn call_workspace_action( &self, req: CallWorkspaceActionRequest, ) -> impl Future>; fn call_folder_action( &self, req: CallFolderActionRequest, ) -> impl Future>; // -- Authentication -- fn http_authentication_summaries( &self, ) -> impl Future>>; /// The form an auth plugin wants to show. `values` arrive already rendered. fn http_authentication_config( &self, auth_name: &str, values: HashMap, model_id: &str, ) -> impl Future>; fn call_http_authentication_action( &self, auth_name: &str, action_index: i32, values: HashMap, model_id: &str, ) -> impl Future>; // -- The importers, and the runtime itself -- /// Hand arbitrary text to the importer plugins and take what they make of /// it. Used for files, URLs and pasted `curl` commands alike. fn import_data(&self, content: &str) -> impl Future>; /// Restart every plugin, returning `(plugin, error)` for those that failed. fn reload_plugins(&self, plugins: Vec) -> impl Future>; /// Re-encrypt the `secure(...)` values in a template. /// /// Whole operation rather than its pieces because the encryption is only /// half of it: the value is also run through the plugin template functions, /// so this needs the plugin runtime and not just a key. fn encrypt_secure_template( &self, template: &str, ) -> impl Future>; }