Dynamic plugins (#68)

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

View File

@@ -0,0 +1,12 @@
CREATE TABLE plugins
(
id TEXT NOT NULL
PRIMARY KEY,
model TEXT DEFAULT 'plugin' NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
checked_at DATETIME NULL,
enabled BOOLEAN NOT NULL,
directory TEXT NULL NOT NULL,
url TEXT NULL
);

View File

@@ -28,6 +28,7 @@ pub enum AnalyticsResource {
HttpRequest,
HttpResponse,
KeyValue,
Plugin,
Setting,
Sidebar,
Theme,

View File

@@ -40,7 +40,7 @@ use crate::updates::{UpdateMode, YaakUpdater};
use crate::window_menu::app_menu;
use yaak_models::models::{
CookieJar, Environment, EnvironmentVariable, Folder, GrpcConnection, GrpcEvent, GrpcEventType,
GrpcRequest, HttpRequest, HttpResponse, KeyValue, ModelType, Settings, Workspace,
GrpcRequest, HttpRequest, HttpResponse, KeyValue, ModelType, Plugin, Settings, Workspace,
};
use yaak_models::queries::{
cancel_pending_grpc_connections, cancel_pending_responses, create_default_http_response,
@@ -49,16 +49,17 @@ use yaak_models::queries::{
delete_http_response, delete_workspace, duplicate_grpc_request, duplicate_http_request,
generate_model_id, get_cookie_jar, get_environment, get_folder, get_grpc_connection,
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_http_responses, list_workspaces, set_key_value_raw, update_response_if_id,
get_or_create_settings, get_plugin, get_workspace, list_cookie_jars, list_environments,
list_folders, list_grpc_connections, list_grpc_events, list_grpc_requests, list_http_requests,
list_http_responses, list_plugins, 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,
upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace,
};
use yaak_plugin_runtime::events::{
CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse,
InternalEvent, InternalEventPayload, RenderHttpRequestResponse, SendHttpRequestResponse,
InternalEvent, InternalEventPayload, PluginBootResponse, RenderHttpRequestResponse,
SendHttpRequestResponse,
};
use yaak_templates::{Parser, Tokens};
@@ -77,6 +78,9 @@ mod window_menu;
const DEFAULT_WINDOW_WIDTH: f64 = 1100.0;
const DEFAULT_WINDOW_HEIGHT: f64 = 600.0;
const MIN_WINDOW_WIDTH: f64 = 300.0;
const MIN_WINDOW_HEIGHT: f64 = 300.0;
#[derive(serde::Serialize)]
#[serde(default, rename_all = "camelCase")]
struct AppMetaData {
@@ -1165,6 +1169,20 @@ async fn cmd_create_workspace(name: &str, w: WebviewWindow) -> Result<Workspace,
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_create_plugin(directory: &str, url: Option<String>, w: WebviewWindow) -> Result<Plugin, String> {
upsert_plugin(
&w,
Plugin {
directory: directory.into(),
url,
..Default::default()
},
)
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_update_cookie_jar(
cookie_jar: CookieJar,
@@ -1408,10 +1426,9 @@ async fn cmd_list_grpc_requests(
workspace_id: &str,
w: WebviewWindow,
) -> Result<Vec<GrpcRequest>, String> {
let requests = list_grpc_requests(&w, workspace_id)
list_grpc_requests(&w, workspace_id)
.await
.map_err(|e| e.to_string())?;
Ok(requests)
.map_err(|e| e.to_string())
}
#[tauri::command]
@@ -1419,11 +1436,9 @@ async fn cmd_list_http_requests(
workspace_id: &str,
w: WebviewWindow,
) -> Result<Vec<HttpRequest>, String> {
let requests = list_http_requests(&w, workspace_id)
list_http_requests(&w, workspace_id)
.await
.expect("Failed to find requests");
// .map_err(|e| e.to_string())
Ok(requests)
.map_err(|e| e.to_string())
}
#[tauri::command]
@@ -1431,11 +1446,27 @@ async fn cmd_list_environments(
workspace_id: &str,
w: WebviewWindow,
) -> Result<Vec<Environment>, String> {
let environments = list_environments(&w, workspace_id)
list_environments(&w, workspace_id)
.await
.expect("Failed to find environments");
.map_err(|e| e.to_string())
}
Ok(environments)
#[tauri::command]
async fn cmd_list_plugins(w: WebviewWindow) -> Result<Vec<Plugin>, String> {
list_plugins(&w).await.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_plugin_info(
id: &str,
w: WebviewWindow,
plugin_manager: State<'_, PluginManager>,
) -> Result<PluginBootResponse, String> {
let plugin = get_plugin(&w, id).await.map_err(|e| e.to_string())?;
plugin_manager
.get_plugin_info(plugin.directory.as_str())
.await
.ok_or("Failed to find plugin info".to_string())
}
#[tauri::command]
@@ -1689,6 +1720,7 @@ pub fn run() {
cmd_create_folder,
cmd_create_grpc_request,
cmd_create_http_request,
cmd_create_plugin,
cmd_create_workspace,
cmd_curl_to_request,
cmd_delete_all_grpc_connections,
@@ -1730,6 +1762,8 @@ pub fn run() {
cmd_list_grpc_requests,
cmd_list_http_requests,
cmd_list_http_responses,
cmd_list_plugins,
cmd_plugin_info,
cmd_list_workspaces,
cmd_metadata,
cmd_new_nested_window,
@@ -1831,6 +1865,7 @@ fn create_nested_window(
.title(title)
.parent(&window)
.unwrap()
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
.inner_size(DEFAULT_WINDOW_WIDTH * 0.7, DEFAULT_WINDOW_HEIGHT * 0.9);
// Add macOS-only things
@@ -1841,7 +1876,7 @@ fn create_nested_window(
.title_bar_style(TitleBarStyle::Overlay);
}
// Add non-MacOS things
// Add non-macOS things
#[cfg(not(target_os = "macos"))]
{
win_builder = win_builder.decorations(false);
@@ -1874,7 +1909,7 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
100.0 + random::<f64>() * 30.0,
100.0 + random::<f64>() * 30.0,
)
.min_inner_size(300.0, 300.0)
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
.title(handle.package_info().name.to_string());
// Add macOS-only things
@@ -1900,7 +1935,8 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
return;
}
match event.id().0.as_str() {
let event_id = event.id().0.as_str();
match event_id {
"quit" => exit(0),
"close" => w.close().unwrap(),
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
@@ -1971,7 +2007,7 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
}
async fn handle_plugin_event<R: Runtime>(app_handle: &AppHandle<R>, event: &InternalEvent) {
info!("Got event to app {}", event.id);
// info!("Got event to app {}", event.id);
let response_event: Option<InternalEventPayload> = match event.clone().payload {
InternalEventPayload::CopyTextRequest(req) => {
app_handle

View File

@@ -655,6 +655,7 @@ pub struct GrpcEvent {
pub request_id: String,
pub connection_id: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub content: String,
pub event_type: GrpcEventType,
pub metadata: HashMap<String, String>,
@@ -693,6 +694,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent {
request_id: r.get("request_id")?,
connection_id: r.get("connection_id")?,
created_at: r.get("created_at")?,
updated_at: r.get("updated_at")?,
content: r.get("content")?,
event_type: serde_json::from_str(event_type.as_str()).unwrap_or_default(),
metadata: serde_json::from_str(metadata.as_str()).unwrap_or_default(),
@@ -702,6 +704,51 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
#[serde(default, rename_all = "camelCase")]
pub struct Plugin {
pub id: String,
#[ts(type = "\"plugin\"")]
pub model: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub checked_at: Option<NaiveDateTime>,
pub directory: String,
pub url: Option<String>,
pub enabled: bool,
}
#[derive(Iden)]
pub enum PluginIden {
#[iden = "plugins"]
Table,
Id,
Model,
CreatedAt,
UpdatedAt,
CheckedAt,
Directory,
Url,
Enabled,
}
impl<'s> TryFrom<&Row<'s>> for Plugin {
type Error = rusqlite::Error;
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
Ok(Plugin {
id: r.get("id")?,
model: r.get("model")?,
created_at: r.get("created_at")?,
updated_at: r.get("updated_at")?,
checked_at: r.get("checked_at")?,
url: r.get("url")?,
directory: r.get("directory")?,
enabled: r.get("enabled")?,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
#[serde(default, rename_all = "camelCase")]
pub struct KeyValue {
@@ -758,6 +805,7 @@ pub enum ModelType {
TypeGrpcRequest,
TypeHttpRequest,
TypeHttpResponse,
TypePlugin,
TypeWorkspace,
}
@@ -772,6 +820,7 @@ impl ModelType {
ModelType::TypeGrpcRequest => "gr",
ModelType::TypeHttpRequest => "rq",
ModelType::TypeHttpResponse => "rs",
ModelType::TypePlugin => "pg",
ModelType::TypeWorkspace => "wk",
}
.to_string()

View File

@@ -1,12 +1,7 @@
use std::fs;
use crate::error::Result;
use crate::models::{
CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection,
GrpcConnectionIden, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest,
HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, KeyValue, KeyValueIden,
ModelType, Settings, SettingsIden, Workspace, WorkspaceIden,
};
use crate::models::{CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection, GrpcConnectionIden, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, KeyValue, KeyValueIden, ModelType, Plugin, PluginIden, Settings, SettingsIden, Workspace, WorkspaceIden};
use crate::plugin::SqliteConnection;
use log::{debug, error};
use rand::distributions::{Alphanumeric, DistString};
@@ -848,7 +843,7 @@ pub async fn upsert_environment<R: Runtime>(
serde_json::to_string(&environment.variables)?.into(),
])
.on_conflict(
OnConflict::column(GrpcEventIden::Id)
OnConflict::column(EnvironmentIden::Id)
.update_columns([
EnvironmentIden::UpdatedAt,
EnvironmentIden::Name,
@@ -877,6 +872,88 @@ pub async fn get_environment<R: Runtime>(mgr: &impl Manager<R>, id: &str) -> Res
Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?)
}
pub async fn get_plugin<R: Runtime>(
mgr: &impl Manager<R>,
id: &str
) -> Result<Plugin> {
let dbm = &*mgr.state::<SqliteConnection>();
let db = dbm.0.lock().await.get().unwrap();
let (sql, params) = Query::select()
.from(PluginIden::Table)
.column(Asterisk)
.cond_where(Expr::col(EnvironmentIden::Id).eq(id))
.build_rusqlite(SqliteQueryBuilder);
let mut stmt = db.prepare(sql.as_str())?;
Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?)
}
pub async fn list_plugins<R: Runtime>(
mgr: &impl Manager<R>,
) -> Result<Vec<Plugin>> {
let dbm = &*mgr.state::<SqliteConnection>();
let db = dbm.0.lock().await.get().unwrap();
let (sql, params) = Query::select()
.from(PluginIden::Table)
.column(Asterisk)
.order_by(PluginIden::CreatedAt, Order::Desc)
.build_rusqlite(SqliteQueryBuilder);
let mut stmt = db.prepare(sql.as_str())?;
let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?;
Ok(items.map(|v| v.unwrap()).collect())
}
pub async fn upsert_plugin<R: Runtime>(
window: &WebviewWindow<R>,
plugin: Plugin,
) -> Result<Plugin> {
let id = match plugin.id.as_str() {
"" => generate_model_id(ModelType::TypePlugin),
_ => plugin.id.to_string(),
};
let dbm = &*window.app_handle().state::<SqliteConnection>();
let db = dbm.0.lock().await.get().unwrap();
let (sql, params) = Query::insert()
.into_table(PluginIden::Table)
.columns([
PluginIden::Id,
PluginIden::CreatedAt,
PluginIden::UpdatedAt,
PluginIden::CheckedAt,
PluginIden::Directory,
PluginIden::Url,
PluginIden::Enabled,
])
.values_panic([
id.as_str().into(),
CurrentTimestamp.into(),
CurrentTimestamp.into(),
plugin.checked_at.into(),
plugin.directory.into(),
plugin.url.into(),
plugin.enabled.into(),
])
.on_conflict(
OnConflict::column(PluginIden::Id)
.update_columns([
PluginIden::UpdatedAt,
PluginIden::CheckedAt,
PluginIden::Directory,
PluginIden::Url,
PluginIden::Enabled,
])
.to_owned(),
)
.returning_all()
.build_rusqlite(SqliteQueryBuilder);
let mut stmt = db.prepare(sql.as_str())?;
let m = stmt.query_row(&*params.as_params(), |row| row.try_into())?;
Ok(emit_upserted_model(window, m))
}
pub async fn get_folder<R: Runtime>(mgr: &impl Manager<R>, id: &str) -> Result<Folder> {
let dbm = &*mgr.state::<SqliteConnection>();
let db = dbm.0.lock().await.get().unwrap();

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,