Dynamic plugins (#68)

This commit is contained in:
Gregory Schier
2024-09-06 10:43:25 -07:00
committed by GitHub
parent e4e888c47a
commit c02aa4f2d0
37 changed files with 533 additions and 184 deletions

View File

@@ -2,10 +2,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use ts_rs::TS;
use yaak_models::models::{
CookieJar, Environment, Folder, GrpcConnection, GrpcEvent, GrpcRequest, HttpRequest,
HttpResponse, KeyValue, Settings, Workspace,
};
use yaak_models::models::{CookieJar, Environment, Folder, GrpcConnection, GrpcEvent, GrpcRequest, HttpRequest, HttpResponse, KeyValue, Plugin, Settings, Workspace};
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]
@@ -21,8 +18,8 @@ pub struct InternalEvent {
#[serde(rename_all = "snake_case", tag = "type")]
#[ts(export)]
pub enum InternalEventPayload {
BootRequest(BootRequest),
BootResponse(BootResponse),
BootRequest(PluginBootRequest),
BootResponse(PluginBootResponse),
ImportRequest(ImportRequest),
ImportResponse(ImportResponse),
@@ -71,14 +68,14 @@ pub struct EmptyResponse {}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export)]
pub struct BootRequest {
pub struct PluginBootRequest {
pub dir: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export)]
pub struct BootResponse {
pub struct PluginBootResponse {
pub name: String,
pub version: String,
pub capabilities: Vec<String>,
@@ -401,4 +398,5 @@ pub enum Model {
Workspace(Workspace),
CookieJar(CookieJar),
Settings(Settings),
Plugin(Plugin),
}

View File

@@ -1,9 +1,9 @@
use crate::error::Result;
use crate::events::{
CallHttpRequestActionRequest, CallTemplateFunctionArgs, RenderPurpose,
PluginBootResponse, CallHttpRequestActionRequest, CallTemplateFunctionArgs,
CallTemplateFunctionRequest, CallTemplateFunctionResponse, FilterRequest, FilterResponse,
GetHttpRequestActionsRequest, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload,
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose,
};
use std::collections::HashMap;
@@ -64,6 +64,10 @@ impl PluginManager {
.await
}
pub async fn get_plugin_info(&self, dir: &str) -> Option<PluginBootResponse> {
self.server.plugin_by_dir(dir).await.ok()?.info().await
}
pub async fn get_http_request_actions(&self) -> Result<Vec<GetHttpRequestActionsResponse>> {
let reply_events = self
.server
@@ -155,7 +159,9 @@ impl PluginManager {
});
match result {
None => Err(PluginErr("No importers found for file contents".to_string())),
None => Err(PluginErr(
"No importers found for file contents".to_string(),
)),
Some((resp, ref_id)) => {
let plugin = self.server.plugin_by_ref_id(ref_id.as_str()).await?;
let plugin_name = plugin.name().await;

View File

@@ -15,21 +15,30 @@ use tokio::fs::read_dir;
use tokio::net::TcpListener;
use tonic::codegen::tokio_stream;
use tonic::transport::Server;
use yaak_models::queries::list_plugins;
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("yaak_plugin_runtime")
.setup(|app, _| {
let plugins_dir = app
.setup(|app_handle, _| {
let plugins_dir = app_handle
.path()
.resolve("plugins", BaseDirectory::Resource)
.expect("failed to resolve plugin directory resource");
tauri::async_runtime::block_on(async move {
let plugin_dirs = read_plugins_dir(&plugins_dir)
let bundled_plugin_dirs = read_plugins_dir(&plugins_dir)
.await
.expect(format!("Failed to read plugins dir: {:?}", plugins_dir).as_str());
let manager = PluginManager::new(&app, plugin_dirs).await;
app.manage(manager);
let plugins = list_plugins(app_handle).await.unwrap_or_default();
let installed_plugin_dirs = plugins
.iter()
.map(|p| p.directory.to_owned())
.collect::<Vec<String>>();
let plugin_dirs = [installed_plugin_dirs, bundled_plugin_dirs].concat();
let manager = PluginManager::new(&app_handle, plugin_dirs).await;
app_handle.manage(manager);
Ok(())
})
})

View File

@@ -1,4 +1,3 @@
use log::info;
use rand::distributions::{Alphanumeric, DistString};
use std::collections::HashMap;
use std::pin::Pin;
@@ -11,7 +10,7 @@ use tonic::{Request, Response, Status, Streaming};
use crate::error::Error::PluginNotFoundErr;
use crate::error::Result;
use crate::events::{BootRequest, BootResponse, InternalEvent, InternalEventPayload};
use crate::events::{PluginBootRequest, PluginBootResponse, InternalEvent, InternalEventPayload};
use crate::server::plugin_runtime::plugin_runtime_server::PluginRuntime;
use plugin_runtime::EventStreamEvent;
use yaak_models::queries::generate_id;
@@ -28,7 +27,7 @@ pub struct PluginHandle {
dir: String,
to_plugin_tx: Arc<Mutex<mpsc::Sender<tonic::Result<EventStreamEvent>>>>,
ref_id: String,
boot_resp: Arc<Mutex<Option<BootResponse>>>,
boot_resp: Arc<Mutex<Option<PluginBootResponse>>>,
}
impl PluginHandle {
@@ -38,6 +37,11 @@ impl PluginHandle {
Some(r) => r.name.to_owned(),
}
}
pub async fn info(&self) -> Option<PluginBootResponse> {
let resp = &*self.boot_resp.lock().await;
resp.clone()
}
pub fn build_event_to_send(
&self,
@@ -53,11 +57,11 @@ impl PluginHandle {
}
pub async fn send(&self, event: &InternalEvent) -> Result<()> {
info!(
"Sending event to plugin {} {:?}",
event.id,
self.name().await
);
// info!(
// "Sending event to plugin {} {:?}",
// event.id,
// self.name().await
// );
self.to_plugin_tx
.lock()
.await
@@ -68,7 +72,7 @@ impl PluginHandle {
Ok(())
}
pub async fn boot(&self, resp: &BootResponse) {
pub async fn boot(&self, resp: &PluginBootResponse) {
let mut boot_resp = self.boot_resp.lock().await;
*boot_resp = Some(resp.clone());
}
@@ -120,7 +124,7 @@ impl PluginRuntimeGrpcServer {
};
}
pub async fn boot_plugin(&self, id: &str, resp: &BootResponse) {
pub async fn boot_plugin(&self, id: &str, resp: &PluginBootResponse) {
match self.plugin_ref_to_plugin.lock().await.get(id) {
None => {
println!("Tried booting non-existing plugin {}", id);
@@ -151,61 +155,27 @@ impl PluginRuntimeGrpcServer {
plugin_handle
}
// pub async fn callback(
// &self,
// source_event: InternalEvent,
// payload: InternalEventPayload,
// ) -> Result<InternalEvent> {
// let reply_id = match source_event.clone().reply_id {
// None => {
// let msg = format!("Source event missing reply Id {:?}", source_event.clone());
// return Err(MissingCallbackIdErr(msg));
// }
// Some(id) => id,
// };
//
// let callbacks = self.callbacks.lock().await;
// let plugin_name = match callbacks.get(reply_id.as_str()) {
// None => {
// let msg = format!("Callback not found {:?}", source_event);
// return Err(MissingCallbackErr(msg));
// }
// Some(n) => n,
// };
//
// let plugins = self.plugins.lock().await;
// let plugin = match plugins.get(plugin_name) {
// None => {
// let msg = format!(
// "Plugin not found {plugin_name}. Choices were {:?}",
// plugins.keys()
// );
// return Err(UnknownPluginErr(msg));
// }
// Some(n) => n,
// };
//
// plugin.send(&payload, Some(reply_id)).await
// }
pub async fn plugin_by_ref_id(&self, ref_id: &str) -> Result<PluginHandle> {
let plugins = self.plugin_ref_to_plugin.lock().await;
if plugins.is_empty() {
return Err(PluginNotFoundErr(ref_id.into()));
}
match plugins.get(ref_id) {
None => Err(PluginNotFoundErr(ref_id.into())),
Some(p) => Ok(p.to_owned()),
}
}
pub async fn plugin_by_dir(&self, dir: &str) -> Result<PluginHandle> {
let plugins = self.plugin_ref_to_plugin.lock().await;
for p in plugins.values() {
if p.dir == dir {
return Ok(p.to_owned());
}
}
Err(PluginNotFoundErr(dir.into()))
}
pub async fn plugin_by_name(&self, plugin_name: &str) -> Result<PluginHandle> {
let plugins = self.plugin_ref_to_plugin.lock().await;
if plugins.is_empty() {
return Err(PluginNotFoundErr(plugin_name.into()));
}
for p in plugins.values() {
if p.name().await == plugin_name {
return Ok(p.to_owned());
@@ -341,7 +311,7 @@ impl PluginRuntimeGrpcServer {
plugin_ids.push(plugin.clone().ref_id);
let event = plugin.build_event_to_send(
&InternalEventPayload::BootRequest(BootRequest {
&InternalEventPayload::BootRequest(PluginBootRequest {
dir: dir.to_string(),
}),
None,