From 4c4eaba7d220657855177853b95e1e984fb97f76 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Thu, 20 Mar 2025 09:43:14 -0700 Subject: [PATCH] Queries now use AppHandle instead of Window (#189) --- src-tauri/src/history.rs | 32 +- src-tauri/src/http_request.rs | 110 ++- src-tauri/src/lib.rs | 685 +++++++++++------- src-tauri/src/notifications.rs | 22 +- src-tauri/src/plugin_events.rs | 12 +- src-tauri/yaak-license/src/license.rs | 22 +- src-tauri/yaak-models/bindings/gen_models.ts | 4 +- src-tauri/yaak-models/src/error.rs | 6 + src-tauri/yaak-models/src/lib.rs | 1 + src-tauri/yaak-models/src/manager.rs | 40 + src-tauri/yaak-models/src/queries.rs | 591 +++++++-------- src-tauri/yaak-sync/src/commands.rs | 22 +- src-tauri/yaak-sync/src/sync.rs | 57 +- src-tauri/yaak-templates/src/renderer.rs | 11 +- src-tauri/yaak-ws/src/commands.rs | 99 +-- .../components/GrpcConnectionSetupPane.tsx | 15 +- src-web/components/HttpRequestPane.tsx | 23 +- src-web/components/WebsocketRequestPane.tsx | 20 +- src-web/hooks/useSyncModelStores.ts | 31 +- 19 files changed, 1063 insertions(+), 740 deletions(-) create mode 100644 src-tauri/yaak-models/src/manager.rs diff --git a/src-tauri/src/history.rs b/src-tauri/src/history.rs index 0e430bfa..7bc5ff35 100644 --- a/src-tauri/src/history.rs +++ b/src-tauri/src/history.rs @@ -1,8 +1,7 @@ -use tauri::{Manager, Runtime, WebviewWindow}; +use tauri::{AppHandle, Runtime}; use yaak_models::queries::{ - get_key_value_int, get_key_value_string, - set_key_value_int, set_key_value_string, UpdateSource, + get_key_value_int, get_key_value_string, set_key_value_int, set_key_value_string, UpdateSource, }; const NAMESPACE: &str = "analytics"; @@ -16,14 +15,15 @@ pub struct LaunchEventInfo { pub num_launches: i32, } -pub async fn store_launch_history(w: &WebviewWindow) -> LaunchEventInfo { +pub async fn store_launch_history(app_handle: &AppHandle) -> LaunchEventInfo { let last_tracked_version_key = "last_tracked_version"; let mut info = LaunchEventInfo::default(); - info.num_launches = get_num_launches(w).await + 1; - info.previous_version = get_key_value_string(w, NAMESPACE, last_tracked_version_key, "").await; - info.current_version = w.package_info().version.to_string(); + info.num_launches = get_num_launches(app_handle).await + 1; + info.previous_version = + get_key_value_string(app_handle, NAMESPACE, last_tracked_version_key, "").await; + info.current_version = app_handle.package_info().version.to_string(); if info.previous_version.is_empty() { } else { @@ -33,16 +33,22 @@ pub async fn store_launch_history(w: &WebviewWindow) -> LaunchEve // Update key values set_key_value_string( - w, + app_handle, NAMESPACE, last_tracked_version_key, info.current_version.as_str(), &UpdateSource::Background, ) .await; - - set_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches, &UpdateSource::Background) - .await; + + set_key_value_int( + app_handle, + NAMESPACE, + NUM_LAUNCHES_KEY, + info.num_launches, + &UpdateSource::Background, + ) + .await; info } @@ -59,6 +65,6 @@ pub fn get_os() -> &'static str { } } -pub async fn get_num_launches(w: &WebviewWindow) -> i32 { - get_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, 0).await +pub async fn get_num_launches(app_handle: &AppHandle) -> i32 { + get_key_value_int(app_handle, NAMESPACE, NUM_LAUNCHES_KEY, 0).await } diff --git a/src-tauri/src/http_request.rs b/src-tauri/src/http_request.rs index d19c9a29..ec2f0902 100644 --- a/src-tauri/src/http_request.rs +++ b/src-tauri/src/http_request.rs @@ -46,18 +46,22 @@ pub async fn send_http_request( cookie_jar: Option, cancelled_rx: &mut Receiver, ) -> Result { - let plugin_manager = window.state::(); - let workspace = get_workspace(window, &unrendered_request.workspace_id).await?; - let base_environment = get_base_environment(window, &unrendered_request.workspace_id).await?; - let settings = get_or_create_settings(window).await; + let app_handle = window.app_handle().clone(); + let plugin_manager = app_handle.state::(); + let workspace = get_workspace(&app_handle, &unrendered_request.workspace_id).await?; + let base_environment = + get_base_environment(&app_handle, &unrendered_request.workspace_id).await?; + let settings = get_or_create_settings(&app_handle).await; + + let response_id = og_response.id.clone(); + let response = Arc::new(Mutex::new(og_response.clone())); + let cb = PluginTemplateCallback::new( window.app_handle(), &WindowContext::from_window(window), RenderPurpose::Send, ); - - let response_id = og_response.id.clone(); - let response = Arc::new(Mutex::new(og_response.clone())); + let update_source = UpdateSource::from_window(window); let request = match render_http_request( &unrendered_request, @@ -68,7 +72,15 @@ pub async fn send_http_request( .await { Ok(r) => r, - Err(e) => return Ok(response_err(&*response.lock().await, e.to_string(), window).await), + Err(e) => { + return Ok(response_err( + &app_handle, + &*response.lock().await, + e.to_string(), + &update_source, + ) + .await) + } }; let mut url_string = request.url; @@ -178,9 +190,10 @@ pub async fn send_http_request( Ok(u) => u, Err(e) => { return Ok(response_err( + &app_handle, &*response.lock().await, format!("Failed to parse URL \"{}\": {}", url_string, e.to_string()), - window, + &update_source, ) .await); } @@ -190,9 +203,10 @@ pub async fn send_http_request( Ok(u) => u, Err(e) => { return Ok(response_err( + &app_handle, &*response.lock().await, format!("Failed to parse URL \"{}\": {}", url_string, e.to_string()), - window, + &update_source, ) .await); } @@ -296,7 +310,13 @@ pub async fn send_http_request( request_builder = request_builder.body(f); } Err(e) => { - return Ok(response_err(&*response.lock().await, e, window).await); + return Ok(response_err( + &app_handle, + &*response.lock().await, + e, + &update_source, + ) + .await); } } } else if body_type == "multipart/form-data" && request_body.contains_key("form") { @@ -323,9 +343,10 @@ pub async fn send_http_request( Ok(f) => multipart::Part::bytes(f), Err(e) => { return Ok(response_err( + &app_handle, &*response.lock().await, e.to_string(), - window, + &update_source, ) .await); } @@ -340,9 +361,10 @@ pub async fn send_http_request( Ok(p) => p, Err(e) => { return Ok(response_err( + &app_handle, &*response.lock().await, format!("Invalid mime for multi-part entry {e:?}"), - window, + &update_source, ) .await); } @@ -356,9 +378,10 @@ pub async fn send_http_request( Ok(p) => p, Err(e) => { return Ok(response_err( + &app_handle, &*response.lock().await, format!("Invalid mime for multi-part entry {e:?}"), - window, + &update_source, ) .await); } @@ -397,7 +420,13 @@ pub async fn send_http_request( Ok(r) => r, Err(e) => { warn!("Failed to build request builder {e:?}"); - return Ok(response_err(&*response.lock().await, e.to_string(), window).await); + return Ok(response_err( + &app_handle, + &*response.lock().await, + e.to_string(), + &update_source, + ) + .await); } }; @@ -423,7 +452,13 @@ pub async fn send_http_request( let plugin_result = match auth_result { Ok(r) => r, Err(e) => { - return Ok(response_err(&*response.lock().await, e.to_string(), window).await); + return Ok(response_err( + &app_handle, + &*response.lock().await, + e.to_string(), + &update_source, + ) + .await); } }; @@ -449,21 +484,23 @@ pub async fn send_http_request( Ok(r) = resp_rx => r, _ = cancelled_rx.changed() => { debug!("Request cancelled"); - return Ok(response_err(&*response.lock().await, "Request was cancelled".to_string(), window).await); + return Ok(response_err(&app_handle, &*response.lock().await, "Request was cancelled".to_string(), &update_source).await); } }; { + let app_handle = app_handle.clone(); let window = window.clone(); let cancelled_rx = cancelled_rx.clone(); let response_id = response_id.clone(); let response = response.clone(); + let update_source = update_source.clone(); tokio::spawn(async move { match raw_response { Ok(mut v) => { let content_length = v.content_length(); let response_headers = v.headers().clone(); - let dir = window.app_handle().path().app_data_dir().unwrap(); + let dir = app_handle.path().app_data_dir().unwrap(); let base_dir = dir.join("responses"); create_dir_all(base_dir.clone()).await.expect("Failed to create responses dir"); let body_path = if response_id.is_empty() { @@ -497,7 +534,7 @@ pub async fn send_http_request( }; r.state = HttpResponseState::Connected; - update_response_if_id(&window, &r, &UpdateSource::Window) + update_response_if_id(&app_handle, &r, &update_source) .await .expect("Failed to update response after connected"); } @@ -526,7 +563,7 @@ pub async fn send_http_request( f.flush().await.expect("Failed to flush file"); written_bytes += bytes.len(); r.content_length = Some(written_bytes as i32); - update_response_if_id(&window, &r, &UpdateSource::Window) + update_response_if_id(&app_handle, &r, &update_source) .await .expect("Failed to update response"); } @@ -534,7 +571,13 @@ pub async fn send_http_request( break; } Err(e) => { - response_err(&*response.lock().await, e.to_string(), &window).await; + response_err( + &app_handle, + &*response.lock().await, + e.to_string(), + &update_source, + ) + .await; break; } } @@ -548,7 +591,7 @@ pub async fn send_http_request( None => Some(written_bytes as i32), }; r.state = HttpResponseState::Closed; - update_response_if_id(&window, &r, &UpdateSource::Window) + update_response_if_id(&app_handle, &r, &UpdateSource::from_window(&window)) .await .expect("Failed to update response"); }; @@ -574,8 +617,12 @@ pub async fn send_http_request( }) .collect::>(); cookie_jar.cookies = json_cookies; - if let Err(e) = - upsert_cookie_jar(&window, &cookie_jar, &UpdateSource::Window).await + if let Err(e) = upsert_cookie_jar( + &app_handle, + &cookie_jar, + &UpdateSource::from_window(&window), + ) + .await { error!("Failed to update cookie jar: {}", e); }; @@ -583,7 +630,13 @@ pub async fn send_http_request( } Err(e) => { warn!("Failed to execute request {e}"); - response_err(&*response.lock().await, format!("{e} → {e:?}"), &window).await; + response_err( + &app_handle, + &*response.lock().await, + format!("{e} → {e:?}"), + &update_source, + ) + .await; } }; @@ -592,16 +645,17 @@ pub async fn send_http_request( }); }; + let app_handle = app_handle.clone(); Ok(tokio::select! { Ok(r) = done_rx => r, _ = cancelled_rx.changed() => { - match get_http_response(window, response_id.as_str()).await { + match get_http_response(&app_handle, response_id.as_str()).await { Ok(mut r) => { r.state = HttpResponseState::Closed; - update_response_if_id(&window, &r, &UpdateSource::Window).await.expect("Failed to update response") + update_response_if_id(&app_handle, &r, &UpdateSource::from_window(window)).await.expect("Failed to update response") }, _ => { - response_err(&*response.lock().await, "Ephemeral request was cancelled".to_string(), &window).await + response_err(&app_handle, &*response.lock().await, "Ephemeral request was cancelled".to_string(), &update_source).await }.clone(), } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index a2e17af2..b275ce1a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -124,10 +124,10 @@ async fn cmd_render_template( environment_id: Option<&str>, ) -> YaakResult { let environment = match environment_id { - Some(id) => get_environment(&window, id).await.ok(), + Some(id) => get_environment(&app_handle, id).await.ok(), None => None, }; - let base_environment = get_base_environment(&window, &workspace_id).await?; + let base_environment = get_base_environment(&app_handle, &workspace_id).await?; let result = render_template( template, &base_environment, @@ -155,10 +155,10 @@ async fn cmd_dismiss_notification( async fn cmd_grpc_reflect( request_id: &str, proto_files: Vec, - window: WebviewWindow, + app_handle: AppHandle, grpc_handle: State<'_, Mutex>, ) -> Result, String> { - let req = get_grpc_request(&window, request_id) + let req = get_grpc_request(&app_handle, request_id) .await .map_err(|e| e.to_string())? .ok_or("Failed to find GRPC request")?; @@ -181,24 +181,26 @@ async fn cmd_grpc_go( request_id: &str, environment_id: Option<&str>, proto_files: Vec, + app_handle: AppHandle, window: WebviewWindow, plugin_manager: State<'_, PluginManager>, grpc_handle: State<'_, Mutex>, ) -> YaakResult { let environment = match environment_id { - Some(id) => get_environment(&window, id).await.ok(), + Some(id) => get_environment(&app_handle, id).await.ok(), None => None, }; - let unrendered_request = get_grpc_request(&window, request_id) + let unrendered_request = get_grpc_request(&app_handle, request_id) .await? .ok_or(GenericError("Failed to get GRPC request".to_string()))?; - let base_environment = get_base_environment(&window, &unrendered_request.workspace_id).await?; + let base_environment = + get_base_environment(&app_handle, &unrendered_request.workspace_id).await?; let request = render_grpc_request( &unrendered_request, &base_environment, environment.as_ref(), &PluginTemplateCallback::new( - window.app_handle(), + &app_handle, &WindowContext::from_window(&window), RenderPurpose::Send, ), @@ -242,7 +244,7 @@ async fn cmd_grpc_go( } let conn = upsert_grpc_connection( - &window, + &app_handle, &GrpcConnection { workspace_id: request.workspace_id.clone(), request_id: request.id.clone(), @@ -252,7 +254,7 @@ async fn cmd_grpc_go( url: request.url.clone(), ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await?; @@ -296,14 +298,14 @@ async fn cmd_grpc_go( Ok(c) => c, Err(err) => { upsert_grpc_connection( - &window, + &app_handle, &GrpcConnection { elapsed: start.elapsed().as_millis() as i32, error: Some(err.clone()), state: GrpcConnectionState::Closed, ..conn.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await?; return Ok(conn_id); @@ -322,6 +324,7 @@ async fn cmd_grpc_go( let cb = { let cancelled_rx = cancelled_rx.clone(); + let app_handle = app_handle.clone(); let window = window.clone(); let workspace = base_environment.clone(); let environment = environment.clone(); @@ -346,6 +349,7 @@ async fn cmd_grpc_go( match serde_json::from_str::(ev.payload()) { Ok(IncomingMsg::Message(msg)) => { let window = window.clone(); + let app_handle = app_handle.clone(); let base_msg = base_msg.clone(); let method_desc = method_desc.clone(); let msg = block_in_place(|| { @@ -355,7 +359,7 @@ async fn cmd_grpc_go( &workspace, environment.as_ref(), &PluginTemplateCallback::new( - window.app_handle(), + &app_handle, &WindowContext::from_window(&window), RenderPurpose::Send, ), @@ -370,13 +374,13 @@ async fn cmd_grpc_go( Err(e) => { tauri::async_runtime::spawn(async move { upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { event_type: GrpcEventType::Error, content: e.to_string(), ..base_msg.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -387,13 +391,13 @@ async fn cmd_grpc_go( in_msg_tx.try_send(d_msg).unwrap(); tauri::async_runtime::spawn(async move { upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { content: msg, event_type: GrpcEventType::ClientMessage, ..base_msg.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -411,10 +415,11 @@ async fn cmd_grpc_go( } } }; - let event_handler = window.listen_any(format!("grpc_client_msg_{}", conn.id).as_str(), cb); + let event_handler = app_handle.listen_any(format!("grpc_client_msg_{}", conn.id).as_str(), cb); let grpc_listen = { let window = window.clone(); + let app_handle = app_handle.clone(); let base_event = base_msg.clone(); let req = request.clone(); let msg = if req.message.is_empty() { "{}".to_string() } else { req.message }; @@ -423,7 +428,7 @@ async fn cmd_grpc_go( &base_environment.clone(), environment.as_ref(), &PluginTemplateCallback::new( - window.app_handle(), + &app_handle, &WindowContext::from_window(&window), RenderPurpose::Send, ), @@ -431,14 +436,14 @@ async fn cmd_grpc_go( .await?; upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { content: format!("Connecting to {}", req.url), event_type: GrpcEventType::ConnectionStart, metadata: metadata.clone(), ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await?; @@ -470,13 +475,13 @@ async fn cmd_grpc_go( if !method_desc.is_client_streaming() { upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { event_type: GrpcEventType::ClientMessage, content: msg, ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -485,7 +490,7 @@ async fn cmd_grpc_go( match maybe_msg { Some(Ok(msg)) => { upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { metadata: metadata_to_map(msg.metadata().clone()), content: if msg.metadata().len() == 0 { @@ -497,37 +502,37 @@ async fn cmd_grpc_go( event_type: GrpcEventType::Info, ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { content: serialize_message(&msg.into_inner()).unwrap(), event_type: GrpcEventType::ServerMessage, ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { content: "Connection complete".to_string(), event_type: GrpcEventType::ConnectionEnd, status: Some(Code::Ok as i32), ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); } Some(Err(e)) => { upsert_grpc_event( - &window, + &app_handle, &(match e.status { Some(s) => GrpcEvent { error: Some(s.message().to_string()), @@ -545,7 +550,7 @@ async fn cmd_grpc_go( ..base_event.clone() }, }), - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -558,7 +563,7 @@ async fn cmd_grpc_go( let mut stream = match maybe_stream { Some(Ok(stream)) => { upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { metadata: metadata_to_map(stream.metadata().clone()), content: if stream.metadata().len() == 0 { @@ -570,7 +575,7 @@ async fn cmd_grpc_go( event_type: GrpcEventType::Info, ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -579,7 +584,7 @@ async fn cmd_grpc_go( Some(Err(e)) => { warn!("GRPC stream error {e:?}"); upsert_grpc_event( - &window, + &app_handle, &(match e.status { Some(s) => GrpcEvent { error: Some(s.message().to_string()), @@ -597,7 +602,7 @@ async fn cmd_grpc_go( ..base_event.clone() }, }), - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -611,13 +616,13 @@ async fn cmd_grpc_go( Ok(Some(msg)) => { let message = serialize_message(&msg).unwrap(); upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { content: message, event_type: GrpcEventType::ServerMessage, ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -626,7 +631,7 @@ async fn cmd_grpc_go( let trailers = stream.trailers().await.unwrap_or_default().unwrap_or_default(); upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { content: "Connection complete".to_string(), status: Some(Code::Ok as i32), @@ -634,7 +639,7 @@ async fn cmd_grpc_go( event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -642,7 +647,7 @@ async fn cmd_grpc_go( } Err(status) => { upsert_grpc_event( - &window, + &app_handle, &GrpcEvent { content: status.to_string(), status: Some(status.code() as i32), @@ -650,7 +655,7 @@ async fn cmd_grpc_go( event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -663,7 +668,7 @@ async fn cmd_grpc_go( { let conn_id = conn_id.clone(); tauri::async_runtime::spawn(async move { - let w = window.clone(); + let w = app_handle.clone(); tokio::select! { _ = grpc_listen => { let events = list_grpc_events(&w, &conn_id) @@ -681,7 +686,7 @@ async fn cmd_grpc_go( state: GrpcConnectionState::Closed, ..get_grpc_connection(&w, &conn_id).await.unwrap().clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ).await.unwrap(); }, _ = cancelled_rx.changed() => { @@ -693,7 +698,7 @@ async fn cmd_grpc_go( status: Some(Code::Cancelled as i32), ..base_msg.clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ).await.unwrap(); upsert_grpc_connection( &w, @@ -703,7 +708,7 @@ async fn cmd_grpc_go( state: GrpcConnectionState::Closed, ..get_grpc_connection(&w, &conn_id).await.unwrap().clone() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -717,20 +722,23 @@ async fn cmd_grpc_go( } #[tauri::command] -async fn cmd_send_ephemeral_request( +async fn cmd_send_ephemeral_request( mut request: HttpRequest, environment_id: Option<&str>, cookie_jar_id: Option<&str>, window: WebviewWindow, + app_handle: AppHandle, ) -> YaakResult { let response = HttpResponse::new(); request.id = "".to_string(); let environment = match environment_id { - Some(id) => Some(get_environment(&window, id).await.expect("Failed to get environment")), + Some(id) => { + Some(get_environment(&app_handle, id).await.expect("Failed to get environment")) + } None => None, }; let cookie_jar = match cookie_jar_id { - Some(id) => Some(get_cookie_jar(&window, id).await.expect("Failed to get cookie jar")), + Some(id) => Some(get_cookie_jar(&app_handle, id).await.expect("Failed to get cookie jar")), None => None, }; @@ -752,12 +760,13 @@ async fn cmd_format_json(text: &str) -> Result { #[tauri::command] async fn cmd_filter_response( window: WebviewWindow, + app_handle: AppHandle, response_id: &str, plugin_manager: State<'_, PluginManager>, filter: &str, ) -> Result { let response = - get_http_response(&window, response_id).await.expect("Failed to get http response"); + get_http_response(&app_handle, response_id).await.expect("Failed to get http response"); if let None = response.body_path { return Err("Response body path not set".to_string()); @@ -804,6 +813,7 @@ async fn cmd_get_sse_events(file_path: &str) -> Result, Str #[tauri::command] async fn cmd_import_data( window: WebviewWindow, + app_handle: AppHandle, plugin_manager: State<'_, PluginManager>, file_path: &str, ) -> Result { @@ -915,7 +925,7 @@ async fn cmd_import_data( .collect(); let upserted = batch_upsert( - &window, + &app_handle, workspaces, environments, folders, @@ -1017,15 +1027,16 @@ async fn cmd_curl_to_request( } #[tauri::command] -async fn cmd_export_data( - window: WebviewWindow, +async fn cmd_export_data( + app_handle: AppHandle, export_path: &str, workspace_ids: Vec<&str>, include_environments: bool, ) -> Result<(), String> { - let export_data = get_workspace_export_resources(&window, workspace_ids, include_environments) - .await - .map_err(|e| e.to_string())?; + let export_data = + get_workspace_export_resources(&app_handle, workspace_ids, include_environments) + .await + .map_err(|e| e.to_string())?; let f = File::options() .create(true) .truncate(true) @@ -1043,12 +1054,12 @@ async fn cmd_export_data( } #[tauri::command] -async fn cmd_save_response( - window: WebviewWindow, +async fn cmd_save_response( + app_handle: AppHandle, response_id: &str, filepath: &str, ) -> Result<(), String> { - let response = get_http_response(&window, response_id).await.map_err(|e| e.to_string())?; + let response = get_http_response(&app_handle, response_id).await.map_err(|e| e.to_string())?; let body_path = match response.body_path { None => { @@ -1063,8 +1074,9 @@ async fn cmd_save_response( } #[tauri::command] -async fn cmd_send_http_request( - window: WebviewWindow, +async fn cmd_send_http_request( + app_handle: AppHandle, + window: WebviewWindow, environment_id: Option<&str>, cookie_jar_id: Option<&str>, // NOTE: We receive the entire request because to account for the race @@ -1073,17 +1085,18 @@ async fn cmd_send_http_request( request: HttpRequest, ) -> YaakResult { let response = - create_default_http_response(&window, &request.id, &UpdateSource::Window).await?; + create_default_http_response(&app_handle, &request.id, &UpdateSource::from_window(&window)) + .await?; let (cancel_tx, mut cancel_rx) = tokio::sync::watch::channel(false); - window.listen_any(format!("cancel_http_response_{}", response.id), move |_event| { + app_handle.listen_any(format!("cancel_http_response_{}", response.id), move |_event| { if let Err(e) = cancel_tx.send(true) { warn!("Failed to send cancel event for request {e:?}"); } }); let environment = match environment_id { - Some(id) => match get_environment(&window, id).await { + Some(id) => match get_environment(&app_handle, id).await { Ok(env) => Some(env), Err(e) => { warn!("Failed to find environment by id {id} {}", e); @@ -1094,7 +1107,7 @@ async fn cmd_send_http_request( }; let cookie_jar = match cookie_jar_id { - Some(id) => Some(get_cookie_jar(&window, id).await.expect("Failed to get cookie jar")), + Some(id) => Some(get_cookie_jar(&app_handle, id).await.expect("Failed to get cookie jar")), None => None, }; @@ -1102,44 +1115,59 @@ async fn cmd_send_http_request( } async fn response_err( + app_handle: &AppHandle, response: &HttpResponse, error: String, - w: &WebviewWindow, + update_source: &UpdateSource, ) -> HttpResponse { warn!("Failed to send request: {error:?}"); let mut response = response.clone(); response.state = HttpResponseState::Closed; response.error = Some(error.clone()); - response = update_response_if_id(w, &response, &UpdateSource::Window) + response = update_response_if_id(app_handle, &response, update_source) .await .expect("Failed to update response"); response } #[tauri::command] -async fn cmd_set_update_mode(update_mode: &str, w: WebviewWindow) -> Result { - cmd_set_key_value("app", "update_mode", update_mode, w).await.map_err(|e| e.to_string()) +async fn cmd_set_update_mode( + update_mode: &str, + app_handle: AppHandle, + window: WebviewWindow, +) -> YaakResult { + let (key_value, _created) = set_key_value_raw( + &app_handle, + "app", + "update_mode", + update_mode, + &UpdateSource::from_window(&window), + ) + .await; + Ok(key_value) } #[tauri::command] -async fn cmd_get_key_value( +async fn cmd_get_key_value( namespace: &str, key: &str, - w: WebviewWindow, + app_handle: AppHandle, ) -> Result, ()> { - let result = get_key_value_raw(&w, namespace, key).await; + let result = get_key_value_raw(&app_handle, namespace, key).await; Ok(result) } #[tauri::command] -async fn cmd_set_key_value( +async fn cmd_set_key_value( + app_handle: AppHandle, + window: WebviewWindow, namespace: &str, key: &str, value: &str, - w: WebviewWindow, ) -> Result { let (key_value, _created) = - set_key_value_raw(&w, namespace, key, value, &UpdateSource::Window).await; + set_key_value_raw(&app_handle, namespace, key, value, &UpdateSource::from_window(&window)) + .await; Ok(key_value) } @@ -1148,6 +1176,7 @@ async fn cmd_install_plugin( directory: &str, url: Option, plugin_manager: State<'_, PluginManager>, + app_handle: AppHandle, window: WebviewWindow, ) -> Result { plugin_manager @@ -1156,13 +1185,13 @@ async fn cmd_install_plugin( .map_err(|e| e.to_string())?; let plugin = upsert_plugin( - &window, + &app_handle, Plugin { directory: directory.into(), url, ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .map_err(|e| e.to_string())?; @@ -1175,8 +1204,9 @@ async fn cmd_uninstall_plugin( plugin_id: &str, plugin_manager: State<'_, PluginManager>, window: WebviewWindow, + app_handle: AppHandle, ) -> Result { - let plugin = delete_plugin(&window, plugin_id, &UpdateSource::Window) + let plugin = delete_plugin(&app_handle, plugin_id, &UpdateSource::from_window(&window)) .await .map_err(|e| e.to_string())?; @@ -1189,47 +1219,58 @@ async fn cmd_uninstall_plugin( } #[tauri::command] -async fn cmd_update_cookie_jar( +async fn cmd_update_cookie_jar( cookie_jar: CookieJar, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - upsert_cookie_jar(&w, &cookie_jar, &UpdateSource::Window).await.map_err(|e| e.to_string()) + upsert_cookie_jar(&app_handle, &cookie_jar, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_delete_cookie_jar(w: WebviewWindow, cookie_jar_id: &str) -> Result { - delete_cookie_jar(&w, cookie_jar_id, &UpdateSource::Window).await.map_err(|e| e.to_string()) +async fn cmd_delete_cookie_jar( + app_handle: AppHandle, + window: WebviewWindow, + cookie_jar_id: &str, +) -> Result { + delete_cookie_jar(&app_handle, cookie_jar_id, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_create_cookie_jar( +async fn cmd_create_cookie_jar( workspace_id: &str, name: &str, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { upsert_cookie_jar( - &w, + &app_handle, &CookieJar { name: name.to_string(), workspace_id: workspace_id.to_string(), ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_create_environment( +async fn cmd_create_environment( workspace_id: &str, environment_id: Option<&str>, name: &str, variables: Vec, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { upsert_environment( - &w, + &app_handle, Environment { workspace_id: workspace_id.to_string(), environment_id: environment_id.map(|s| s.to_string()), @@ -1237,22 +1278,23 @@ async fn cmd_create_environment( variables, ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_create_grpc_request( +async fn cmd_create_grpc_request( workspace_id: &str, name: &str, sort_priority: f32, folder_id: Option<&str>, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { upsert_grpc_request( - &w, + &app_handle, GrpcRequest { workspace_id: workspace_id.to_string(), name: name.to_string(), @@ -1260,184 +1302,240 @@ async fn cmd_create_grpc_request( sort_priority, ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_duplicate_grpc_request(id: &str, w: WebviewWindow) -> Result { - duplicate_grpc_request(&w, id, &UpdateSource::Window).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_duplicate_folder( - window: WebviewWindow, +async fn cmd_duplicate_grpc_request( id: &str, -) -> Result<(), String> { - let folder = get_folder(&window, id).await.map_err(|e| e.to_string())?; - duplicate_folder(&window, &folder).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_create_http_request( - request: HttpRequest, - w: WebviewWindow, -) -> Result { - upsert_http_request(&w, request, &UpdateSource::Window).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_duplicate_http_request(id: &str, w: WebviewWindow) -> Result { - duplicate_http_request(&w, id, &UpdateSource::Window).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_update_workspace(workspace: Workspace, w: WebviewWindow) -> Result { - upsert_workspace(&w, workspace, &UpdateSource::Window).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_update_workspace_meta( - workspace_meta: WorkspaceMeta, - w: WebviewWindow, -) -> Result { - upsert_workspace_meta(&w, workspace_meta, &UpdateSource::Window) + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + duplicate_grpc_request(&app_handle, id, &UpdateSource::from_window(&window)) .await .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_update_environment( - environment: Environment, - w: WebviewWindow, -) -> Result { - upsert_environment(&w, environment, &UpdateSource::Window).await.map_err(|e| e.to_string()) +async fn cmd_duplicate_folder( + app_handle: AppHandle, + window: WebviewWindow, + id: &str, +) -> YaakResult<()> { + let folder = get_folder(&app_handle, id).await?; + let new_folder = + duplicate_folder(&app_handle, &folder, &UpdateSource::from_window(&window)).await?; + Ok(new_folder) } #[tauri::command] -async fn cmd_update_grpc_request( - request: GrpcRequest, - w: WebviewWindow, -) -> Result { - upsert_grpc_request(&w, request, &UpdateSource::Window).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_update_http_request( +async fn cmd_create_http_request( request: HttpRequest, - window: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - upsert_http_request(&window, request, &UpdateSource::Window).await.map_err(|e| e.to_string()) + upsert_http_request(&app_handle, request, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_delete_grpc_request( - w: WebviewWindow, - request_id: &str, +async fn cmd_duplicate_http_request( + id: &str, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + duplicate_http_request(&app_handle, id, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_update_workspace( + workspace: Workspace, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + upsert_workspace(&app_handle, workspace, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_update_workspace_meta( + workspace_meta: WorkspaceMeta, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + upsert_workspace_meta(&app_handle, workspace_meta, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_update_environment( + environment: Environment, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + upsert_environment(&app_handle, environment, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_update_grpc_request( + request: GrpcRequest, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - delete_grpc_request(&w, request_id, &UpdateSource::Window).await.map_err(|e| e.to_string()) + upsert_grpc_request(&app_handle, request, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_delete_http_request( - w: WebviewWindow, - request_id: &str, +async fn cmd_update_http_request( + request: HttpRequest, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - delete_http_request(&w, request_id, &UpdateSource::Window).await.map_err(|e| e.to_string()) + upsert_http_request(&app_handle, request, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_folders(workspace_id: &str, w: WebviewWindow) -> Result, String> { - list_folders(&w, workspace_id).await.map_err(|e| e.to_string()) +async fn cmd_delete_grpc_request( + app_handle: AppHandle, + request_id: &str, + window: WebviewWindow, +) -> Result { + delete_grpc_request(&app_handle, request_id, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_update_folder(folder: Folder, w: WebviewWindow) -> Result { - upsert_folder(&w, folder, &UpdateSource::Window).await.map_err(|e| e.to_string()) +async fn cmd_delete_http_request( + app_handle: AppHandle, + request_id: &str, + window: WebviewWindow, +) -> Result { + delete_http_request(&app_handle, request_id, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_delete_folder(w: WebviewWindow, folder_id: &str) -> Result { - delete_folder(&w, folder_id, &UpdateSource::Window).await.map_err(|e| e.to_string()) +async fn cmd_list_folders( + workspace_id: &str, + app_handle: AppHandle, +) -> Result, String> { + list_folders(&app_handle, workspace_id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_delete_environment( - w: WebviewWindow, +async fn cmd_update_folder( + folder: Folder, + app_handle: AppHandle, + window: WebviewWindow, +) -> YaakResult { + Ok(upsert_folder(&app_handle, folder, &UpdateSource::from_window(&window)).await?) +} + +#[tauri::command] +async fn cmd_delete_folder( + app_handle: AppHandle, + window: WebviewWindow, + folder_id: &str, +) -> Result { + delete_folder(&app_handle, folder_id, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_delete_environment( + app_handle: AppHandle, + window: WebviewWindow, environment_id: &str, ) -> Result { - delete_environment(&w, environment_id, &UpdateSource::Window).await.map_err(|e| e.to_string()) + delete_environment(&app_handle, environment_id, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_grpc_connections( +async fn cmd_list_grpc_connections( workspace_id: &str, - w: WebviewWindow, + app_handle: AppHandle, ) -> Result, String> { - list_grpc_connections_for_workspace(&w, workspace_id).await.map_err(|e| e.to_string()) + list_grpc_connections_for_workspace(&app_handle, workspace_id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_grpc_events( +async fn cmd_list_grpc_events( connection_id: &str, - w: WebviewWindow, + app_handle: AppHandle, ) -> Result, String> { - list_grpc_events(&w, connection_id).await.map_err(|e| e.to_string()) + list_grpc_events(&app_handle, connection_id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_grpc_requests( +async fn cmd_list_grpc_requests( workspace_id: &str, - w: WebviewWindow, + app_handle: AppHandle, ) -> Result, String> { - list_grpc_requests(&w, workspace_id).await.map_err(|e| e.to_string()) + list_grpc_requests(&app_handle, workspace_id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_http_requests( +async fn cmd_list_http_requests( workspace_id: &str, - w: WebviewWindow, + app_handle: AppHandle, ) -> Result, String> { - list_http_requests(&w, workspace_id).await.map_err(|e| e.to_string()) + list_http_requests(&app_handle, workspace_id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_environments( +async fn cmd_list_environments( workspace_id: &str, - w: WebviewWindow, + app_handle: AppHandle, ) -> Result, String> { // Not sure of a better place to put this... - ensure_base_environment(&w, workspace_id).await.map_err(|e| e.to_string())?; - - list_environments(&w, workspace_id).await.map_err(|e| e.to_string()) + ensure_base_environment(&app_handle, workspace_id).await.map_err(|e| e.to_string())?; + list_environments(&app_handle, workspace_id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_plugins(w: WebviewWindow) -> Result, String> { - list_plugins(&w).await.map_err(|e| e.to_string()) +async fn cmd_list_plugins(app_handle: AppHandle) -> Result, String> { + list_plugins(&app_handle).await.map_err(|e| e.to_string()) } #[tauri::command] async fn cmd_reload_plugins( + app_handle: AppHandle, window: WebviewWindow, plugin_manager: State<'_, PluginManager>, ) -> Result<(), String> { plugin_manager - .initialize_all_plugins(window.app_handle(), &WindowContext::from_window(&window)) + .initialize_all_plugins(&app_handle, &WindowContext::from_window(&window)) .await .map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] -async fn cmd_plugin_info( +async fn cmd_plugin_info( id: &str, - w: WebviewWindow, + app_handle: AppHandle, plugin_manager: State<'_, PluginManager>, ) -> Result { - let plugin = get_plugin(&w, id).await.map_err(|e| e.to_string())?; + let plugin = get_plugin(&app_handle, id).await.map_err(|e| e.to_string())?; Ok(plugin_manager .get_plugin_by_dir(plugin.directory.as_str()) .await @@ -1447,51 +1545,68 @@ async fn cmd_plugin_info( } #[tauri::command] -async fn cmd_get_settings(w: WebviewWindow) -> Result { - Ok(get_or_create_settings(&w).await) +async fn cmd_get_settings(app_handle: AppHandle) -> Result { + Ok(get_or_create_settings(&app_handle).await) } #[tauri::command] -async fn cmd_update_settings(settings: Settings, w: WebviewWindow) -> Result { - update_settings(&w, settings, &UpdateSource::Window).await.map_err(|e| e.to_string()) +async fn cmd_update_settings( + settings: Settings, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + update_settings(&app_handle, settings, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_get_folder(id: &str, w: WebviewWindow) -> Result { - get_folder(&w, id).await.map_err(|e| e.to_string()) +async fn cmd_get_folder(id: &str, app_handle: AppHandle) -> Result { + get_folder(&app_handle, id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_get_grpc_request(id: &str, w: WebviewWindow) -> Result, String> { - get_grpc_request(&w, id).await.map_err(|e| e.to_string()) +async fn cmd_get_grpc_request( + id: &str, + app_handle: AppHandle, +) -> Result, String> { + get_grpc_request(&app_handle, id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_get_http_request(id: &str, w: WebviewWindow) -> Result, String> { - get_http_request(&w, id).await.map_err(|e| e.to_string()) +async fn cmd_get_http_request( + id: &str, + app_handle: AppHandle, +) -> Result, String> { + get_http_request(&app_handle, id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_get_cookie_jar(id: &str, w: WebviewWindow) -> Result { - get_cookie_jar(&w, id).await.map_err(|e| e.to_string()) +async fn cmd_get_cookie_jar( + id: &str, + app_handle: AppHandle, +) -> Result { + get_cookie_jar(&app_handle, id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_cookie_jars( +async fn cmd_list_cookie_jars( workspace_id: &str, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result, String> { - let cookie_jars = list_cookie_jars(&w, workspace_id).await.expect("Failed to find cookie jars"); + let cookie_jars = + list_cookie_jars(&app_handle, workspace_id).await.expect("Failed to find cookie jars"); if cookie_jars.is_empty() { let cookie_jar = upsert_cookie_jar( - &w, + &app_handle, &CookieJar { name: "Default".to_string(), workspace_id: workspace_id.to_string(), ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .expect("Failed to create CookieJar"); @@ -1502,89 +1617,133 @@ async fn cmd_list_cookie_jars( } #[tauri::command] -async fn cmd_list_key_values(window: WebviewWindow) -> Result, String> { - list_key_values_raw(&window).await.map_err(|e| e.to_string()) +async fn cmd_list_key_values( + app_handle: AppHandle, +) -> Result, String> { + list_key_values_raw(&app_handle).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_get_environment(id: &str, window: WebviewWindow) -> Result { - get_environment(&window, id).await.map_err(|e| e.to_string()) +async fn cmd_get_environment( + id: &str, + app_handle: AppHandle, +) -> Result { + get_environment(&app_handle, id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_get_workspace(id: &str, window: WebviewWindow) -> Result { - get_workspace(&window, id).await.map_err(|e| e.to_string()) +async fn cmd_get_workspace( + id: &str, + app_handle: AppHandle, +) -> Result { + get_workspace(&app_handle, id).await.map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_http_responses( +async fn cmd_list_http_responses( workspace_id: &str, limit: Option, - window: WebviewWindow, + app_handle: AppHandle, ) -> Result, String> { - list_http_responses_for_workspace(&window, workspace_id, limit).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_delete_http_response(id: &str, window: WebviewWindow) -> Result { - delete_http_response(&window, id, &UpdateSource::Window).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_delete_grpc_connection( - id: &str, - window: WebviewWindow, -) -> Result { - delete_grpc_connection(&window, id, &UpdateSource::Window).await.map_err(|e| e.to_string()) -} - -#[tauri::command] -async fn cmd_delete_all_grpc_connections( - request_id: &str, - window: WebviewWindow, -) -> Result<(), String> { - delete_all_grpc_connections(&window, request_id, &UpdateSource::Window) + list_http_responses_for_workspace(&app_handle, workspace_id, limit) .await .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_delete_send_history(workspace_id: &str, window: WebviewWindow) -> Result<(), String> { - delete_all_http_responses_for_workspace(&window, workspace_id, &UpdateSource::Window) +async fn cmd_delete_http_response( + id: &str, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + delete_http_response(&app_handle, id, &UpdateSource::from_window(&window)) .await - .map_err(|e| e.to_string())?; - delete_all_grpc_connections_for_workspace(&window, workspace_id, &UpdateSource::Window) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_delete_grpc_connection( + id: &str, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result { + delete_grpc_connection(&app_handle, id, &UpdateSource::from_window(&window)) .await - .map_err(|e| e.to_string())?; - delete_all_websocket_connections_for_workspace(&window, workspace_id, &UpdateSource::Window) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_delete_all_grpc_connections( + request_id: &str, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result<(), String> { + delete_all_grpc_connections(&app_handle, request_id, &UpdateSource::from_window(&window)) .await - .map_err(|e| e.to_string())?; + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_delete_send_history( + workspace_id: &str, + app_handle: AppHandle, + window: WebviewWindow, +) -> Result<(), String> { + delete_all_http_responses_for_workspace( + &app_handle, + workspace_id, + &UpdateSource::from_window(&window), + ) + .await + .map_err(|e| e.to_string())?; + delete_all_grpc_connections_for_workspace( + &app_handle, + workspace_id, + &UpdateSource::from_window(&window), + ) + .await + .map_err(|e| e.to_string())?; + delete_all_websocket_connections_for_workspace( + &app_handle, + workspace_id, + &UpdateSource::from_window(&window), + ) + .await + .map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] -async fn cmd_delete_all_http_responses( +async fn cmd_delete_all_http_responses( request_id: &str, - window: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result<(), String> { - delete_all_http_responses_for_request(&window, request_id, &UpdateSource::Window) - .await - .map_err(|e| e.to_string()) + delete_all_http_responses_for_request( + &app_handle, + request_id, + &UpdateSource::from_window(&window), + ) + .await + .map_err(|e| e.to_string()) } #[tauri::command] -async fn cmd_list_workspaces(window: WebviewWindow) -> Result, String> { - let workspaces = list_workspaces(&window).await.expect("Failed to find workspaces"); +async fn cmd_list_workspaces( + app_handle: AppHandle, + window: WebviewWindow, +) -> Result, String> { + let workspaces = list_workspaces(&app_handle).await.expect("Failed to find workspaces"); if workspaces.is_empty() { let workspace = upsert_workspace( - &window, + &app_handle, Workspace { name: "Yaak".to_string(), setting_follow_redirects: true, setting_validate_certificates: true, ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .expect("Failed to create Workspace"); @@ -1595,12 +1754,13 @@ async fn cmd_list_workspaces(window: WebviewWindow) -> Result, St } #[tauri::command] -async fn cmd_get_workspace_meta( - window: WebviewWindow, +async fn cmd_get_workspace_meta( + app_handle: AppHandle, + window: WebviewWindow, workspace_id: &str, ) -> Result { - let workspace = get_workspace(&window, workspace_id).await.map_err(|e| e.to_string())?; - get_or_create_workspace_meta(&window, &workspace, &UpdateSource::Window) + let workspace = get_workspace(&app_handle, workspace_id).await.map_err(|e| e.to_string())?; + get_or_create_workspace_meta(&app_handle, &workspace, &UpdateSource::from_window(&window)) .await .map_err(|e| e.to_string()) } @@ -1624,8 +1784,14 @@ async fn cmd_new_main_window(app_handle: AppHandle, url: &str) -> Result<(), Str } #[tauri::command] -async fn cmd_delete_workspace(w: WebviewWindow, workspace_id: &str) -> Result { - delete_workspace(&w, workspace_id, &UpdateSource::Window).await.map_err(|e| e.to_string()) +async fn cmd_delete_workspace( + app_handle: AppHandle, + window: WebviewWindow, + workspace_id: &str, +) -> Result { + delete_workspace(&app_handle, workspace_id, &UpdateSource::from_window(&window)) + .await + .map_err(|e| e.to_string()) } #[tauri::command] @@ -1808,8 +1974,9 @@ pub fn run() { match event { RunEvent::Ready => { let w = window::create_main_window(app_handle, "/"); + let h = app_handle.clone(); tauri::async_runtime::spawn(async move { - let info = history::store_launch_history(&w).await; + let info = history::store_launch_history(&h).await; debug!("Launched Yaak {:?}", info); }); @@ -1970,7 +2137,7 @@ fn workspace_id_from_window(window: &WebviewWindow) -> Option(window: &WebviewWindow) -> Option { match workspace_id_from_window(&window) { None => None, - Some(id) => get_workspace(window, id.as_str()).await.ok(), + Some(id) => get_workspace(window.app_handle(), id.as_str()).await.ok(), } } @@ -1983,7 +2150,7 @@ fn environment_id_from_window(window: &WebviewWindow) -> Option(window: &WebviewWindow) -> Option { match environment_id_from_window(&window) { None => None, - Some(id) => get_environment(window, id.as_str()).await.ok(), + Some(id) => get_environment(window.app_handle(), id.as_str()).await.ok(), } } @@ -1996,6 +2163,6 @@ fn cookie_jar_id_from_window(window: &WebviewWindow) -> Option(window: &WebviewWindow) -> Option { match cookie_jar_id_from_window(&window) { None => None, - Some(id) => get_cookie_jar(window, id.as_str()).await.ok(), + Some(id) => get_cookie_jar(window.app_handle(), id.as_str()).await.ok(), } } diff --git a/src-tauri/src/notifications.rs b/src-tauri/src/notifications.rs index 470c06da..fc4a94a7 100644 --- a/src-tauri/src/notifications.rs +++ b/src-tauri/src/notifications.rs @@ -6,7 +6,7 @@ use log::debug; use reqwest::Method; use serde::{Deserialize, Serialize}; use serde_json::Value; -use tauri::{Emitter, Manager, Runtime, WebviewWindow}; +use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow}; use yaak_models::queries::{get_key_value_raw, set_key_value_raw, UpdateSource}; // Check for updates every hour @@ -43,16 +43,18 @@ impl YaakNotifier { } } - pub async fn seen(&mut self, w: &WebviewWindow, id: &str) -> Result<(), String> { - let mut seen = get_kv(w).await?; + pub async fn seen(&mut self, window: &WebviewWindow, id: &str) -> Result<(), String> { + let app_handle = window.app_handle(); + let mut seen = get_kv(app_handle).await?; seen.push(id.to_string()); debug!("Marked notification as seen {}", id); let seen_json = serde_json::to_string(&seen).map_err(|e| e.to_string())?; - set_key_value_raw(w, KV_NAMESPACE, KV_KEY, seen_json.as_str(), &UpdateSource::Window).await; + set_key_value_raw(app_handle, KV_NAMESPACE, KV_KEY, seen_json.as_str(), &UpdateSource::from_window(window)).await; Ok(()) } pub async fn check(&mut self, window: &WebviewWindow) -> Result<(), String> { + let app_handle = window.app_handle(); let ignore_check = self.last_check.elapsed().unwrap().as_secs() < MAX_UPDATE_CHECK_SECONDS; if ignore_check { @@ -61,8 +63,8 @@ impl YaakNotifier { self.last_check = SystemTime::now(); - let num_launches = get_num_launches(window).await; - let info = window.app_handle().package_info().clone(); + let num_launches = get_num_launches(app_handle).await; + let info = app_handle.package_info().clone(); let req = reqwest::Client::default() .request(Method::GET, "https://notify.yaak.app/notifications") .query(&[ @@ -90,14 +92,14 @@ impl YaakNotifier { for notification in notifications { let age = notification.timestamp.signed_duration_since(Utc::now()); - let seen = get_kv(window).await?; + let seen = get_kv(app_handle).await?; if seen.contains(¬ification.id) || (age > Duration::days(2)) { debug!("Already seen notification {}", notification.id); continue; } debug!("Got notification {:?}", notification); - let _ = window.emit_to(window.label(), "notification", notification.clone()); + let _ = app_handle.emit_to(window.label(), "notification", notification.clone()); break; // Only show one notification } @@ -105,8 +107,8 @@ impl YaakNotifier { } } -async fn get_kv(w: &WebviewWindow) -> Result, String> { - match get_key_value_raw(w, "notifications", "seen").await { +async fn get_kv(app_handle: &AppHandle) -> Result, String> { + match get_key_value_raw(app_handle, "notifications", "seen").await { None => Ok(Vec::new()), Some(v) => serde_json::from_str(&v.value).map_err(|e| e.to_string()), } diff --git a/src-tauri/src/plugin_events.rs b/src-tauri/src/plugin_events.rs index c4f203ab..8da381c3 100644 --- a/src-tauri/src/plugin_events.rs +++ b/src-tauri/src/plugin_events.rs @@ -18,8 +18,8 @@ use yaak_models::queries::{ use yaak_plugins::events::{ Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse, GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload, - RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest, - TemplateRenderResponse, WindowContext, WindowNavigateEvent, + RenderHttpRequestResponse, RenderPurpose, SendHttpRequestResponse, SetKeyValueResponse, + ShowToastRequest, TemplateRenderResponse, WindowContext, WindowNavigateEvent, }; use yaak_plugins::manager::PluginManager; use yaak_plugins::plugin_handle::PluginHandle; @@ -80,7 +80,7 @@ pub(crate) async fn handle_plugin_event( .await .expect("Failed to get workspace_id from window URL"); let environment = environment_from_window(&window).await; - let base_environment = get_base_environment(&window, workspace.id.as_str()) + let base_environment = get_base_environment(app_handle, workspace.id.as_str()) .await .expect("Failed to get base environment"); let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose); @@ -104,7 +104,7 @@ pub(crate) async fn handle_plugin_event( .await .expect("Failed to get workspace_id from window URL"); let environment = environment_from_window(&window).await; - let base_environment = get_base_environment(&window, workspace.id.as_str()) + let base_environment = get_base_environment(app_handle, workspace.id.as_str()) .await .expect("Failed to get base environment"); let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose); @@ -145,7 +145,7 @@ pub(crate) async fn handle_plugin_event( updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead ..plugin }; - upsert_plugin(&window, new_plugin, &UpdateSource::Plugin).await.unwrap(); + upsert_plugin(app_handle, new_plugin, &UpdateSource::Plugin).await.unwrap(); } let toast_event = plugin_handle.build_event_to_send( &WindowContext::from_window(&window), @@ -177,7 +177,7 @@ pub(crate) async fn handle_plugin_event( HttpResponse::new() } else { create_default_http_response( - &window, + app_handle, http_request.id.as_str(), &UpdateSource::Plugin, ) diff --git a/src-tauri/yaak-license/src/license.rs b/src-tauri/yaak-license/src/license.rs index f24003b3..e5b2b8ef 100644 --- a/src-tauri/yaak-license/src/license.rs +++ b/src-tauri/yaak-license/src/license.rs @@ -81,11 +81,11 @@ pub async fn activate_license( let body: ActivateLicenseResponsePayload = response.json().await?; yaak_models::queries::set_key_value_string( - window, + window.app_handle(), KV_ACTIVATION_ID_KEY, KV_NAMESPACE, body.activation_id.as_str(), - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await; @@ -100,7 +100,8 @@ pub async fn deactivate_license( window: &WebviewWindow, p: DeactivateLicenseRequestPayload, ) -> Result<()> { - let activation_id = get_activation_id(window).await; + let app_handle = window.app_handle(); + let activation_id = get_activation_id(app_handle).await; let client = reqwest::Client::new(); let path = format!("/licenses/activations/{}/deactivate", activation_id); @@ -119,14 +120,14 @@ pub async fn deactivate_license( } yaak_models::queries::delete_key_value( - window, + app_handle, KV_ACTIVATION_ID_KEY, KV_NAMESPACE, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await; - if let Err(e) = window.emit("license-deactivated", true) { + if let Err(e) = app_handle.emit("license-deactivated", true) { warn!("Failed to emit deactivate-license event: {}", e); } @@ -143,7 +144,10 @@ pub enum LicenseCheckStatus { Trialing { end: NaiveDateTime }, } -pub async fn check_license(app_handle: &AppHandle, payload: CheckActivationRequestPayload) -> Result { +pub async fn check_license( + app_handle: &AppHandle, + payload: CheckActivationRequestPayload, +) -> Result { let activation_id = get_activation_id(app_handle).await; let settings = yaak_models::queries::get_or_create_settings(app_handle).await; let trial_end = settings.created_at.add(Duration::from_secs(TRIAL_SECONDS)); @@ -195,7 +199,7 @@ fn build_url(path: &str) -> String { } } -pub async fn get_activation_id(mgr: &impl Manager) -> String { - yaak_models::queries::get_key_value_string(mgr, KV_ACTIVATION_ID_KEY, KV_NAMESPACE, "") +pub async fn get_activation_id(app_handle: &AppHandle) -> String { + yaak_models::queries::get_key_value_string(app_handle, KV_ACTIVATION_ID_KEY, KV_NAMESPACE, "") .await } diff --git a/src-tauri/yaak-models/bindings/gen_models.ts b/src-tauri/yaak-models/bindings/gen_models.ts index 53b4618e..c42af1e8 100644 --- a/src-tauri/yaak-models/bindings/gen_models.ts +++ b/src-tauri/yaak-models/bindings/gen_models.ts @@ -44,7 +44,7 @@ export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, }; -export type ModelPayload = { model: AnyModel, windowLabel: string, updateSource: UpdateSource, }; +export type ModelPayload = { model: AnyModel, updateSource: UpdateSource, }; export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, enabled: boolean, url: string | null, }; @@ -60,7 +60,7 @@ export type SyncHistory = { model: "sync_history", id: string, workspaceId: stri export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, }; -export type UpdateSource = "sync" | "window" | "plugin" | "background" | "import"; +export type UpdateSource = { "type": "sync" } | { "type": "window", label: string, } | { "type": "plugin" } | { "type": "background" } | { "type": "import" }; export type WebsocketConnection = { model: "websocket_connection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, elapsed: number, error: string | null, headers: Array, state: WebsocketConnectionState, status: number, url: string, }; diff --git a/src-tauri/yaak-models/src/error.rs b/src-tauri/yaak-models/src/error.rs index 9f0fb0ca..27f693f9 100644 --- a/src-tauri/yaak-models/src/error.rs +++ b/src-tauri/yaak-models/src/error.rs @@ -4,10 +4,16 @@ use thiserror::Error; pub enum Error { #[error("SQL error: {0}")] SqlError(#[from] rusqlite::Error), + + #[error("SQL Pool error: {0}")] + SqlPoolError(#[from] r2d2::Error), + #[error("JSON error: {0}")] JsonError(#[from] serde_json::Error), + #[error("Model not found {0}")] ModelNotFound(String), + #[error("unknown error")] Unknown, } diff --git a/src-tauri/yaak-models/src/lib.rs b/src-tauri/yaak-models/src/lib.rs index bcb66425..1b4b1ee7 100644 --- a/src-tauri/yaak-models/src/lib.rs +++ b/src-tauri/yaak-models/src/lib.rs @@ -4,3 +4,4 @@ pub mod queries; pub mod plugin; pub mod render; +pub mod manager; diff --git a/src-tauri/yaak-models/src/manager.rs b/src-tauri/yaak-models/src/manager.rs new file mode 100644 index 00000000..4da71161 --- /dev/null +++ b/src-tauri/yaak-models/src/manager.rs @@ -0,0 +1,40 @@ +use crate::error::Result; +use crate::models::{Workspace, WorkspaceIden}; +use crate::plugin::SqliteConnection; +use r2d2::{Pool, PooledConnection}; +use r2d2_sqlite::SqliteConnectionManager; +use rusqlite::Connection; +use sea_query::{Asterisk, Order, Query, SqliteQueryBuilder}; +use sea_query_rusqlite::RusqliteBinder; +use std::future::Future; +use std::ops::Deref; +use tauri::{AppHandle, Manager, Runtime}; + +pub struct QueryManager { + pool: Pool, +} + +pub trait DBConnection { + fn connect( + &self, + ) -> impl Future>> + Send; +} + +impl DBConnection for AppHandle { + async fn connect(&self) -> Result> { + let dbm = &*self.state::(); + let db = dbm.0.lock().await.get()?; + Ok(db) + } +} + +pub async fn list_workspaces>(c: &T) -> Result> { + let (sql, params) = Query::select() + .from(WorkspaceIden::Table) + .column(Asterisk) + .order_by(WorkspaceIden::Name, Order::Asc) + .build_rusqlite(SqliteQueryBuilder); + let mut stmt = c.prepare(sql.as_str())?; + let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?; + Ok(items.map(|v| v.unwrap()).collect()) +} diff --git a/src-tauri/yaak-models/src/queries.rs b/src-tauri/yaak-models/src/queries.rs index 8c6b1ddf..71210bbc 100644 --- a/src-tauri/yaak-models/src/queries.rs +++ b/src-tauri/yaak-models/src/queries.rs @@ -28,34 +28,34 @@ use ts_rs::TS; const MAX_HISTORY_ITEMS: usize = 20; pub async fn set_key_value_string( - mgr: &WebviewWindow, + app_handle: &AppHandle, namespace: &str, key: &str, value: &str, update_source: &UpdateSource, ) -> (KeyValue, bool) { let encoded = serde_json::to_string(value); - set_key_value_raw(mgr, namespace, key, &encoded.unwrap(), update_source).await + set_key_value_raw(app_handle, namespace, key, &encoded.unwrap(), update_source).await } pub async fn set_key_value_int( - mgr: &WebviewWindow, + app_handle: &AppHandle, namespace: &str, key: &str, value: i32, update_source: &UpdateSource, ) -> (KeyValue, bool) { let encoded = serde_json::to_string(&value); - set_key_value_raw(mgr, namespace, key, &encoded.unwrap(), update_source).await + set_key_value_raw(app_handle, namespace, key, &encoded.unwrap(), update_source).await } pub async fn get_key_value_string( - mgr: &impl Manager, + app_handle: &AppHandle, namespace: &str, key: &str, default: &str, ) -> String { - match get_key_value_raw(mgr, namespace, key).await { + match get_key_value_raw(app_handle, namespace, key).await { None => default.to_string(), Some(v) => { let result = serde_json::from_str(&v.value); @@ -71,12 +71,12 @@ pub async fn get_key_value_string( } pub async fn get_key_value_int( - mgr: &impl Manager, + app_handle: &AppHandle, namespace: &str, key: &str, default: i32, ) -> i32 { - match get_key_value_raw(mgr, namespace, key).await { + match get_key_value_raw(app_handle, namespace, key).await { None => default.clone(), Some(v) => { let result = serde_json::from_str(&v.value); @@ -92,15 +92,15 @@ pub async fn get_key_value_int( } pub async fn set_key_value_raw( - w: &WebviewWindow, + app_handle: &AppHandle, namespace: &str, key: &str, value: &str, update_source: &UpdateSource, ) -> (KeyValue, bool) { - let existing = get_key_value_raw(w, namespace, key).await; + let existing = get_key_value_raw(app_handle, namespace, key).await; - let dbm = &*w.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(KeyValueIden::Table) @@ -130,22 +130,22 @@ pub async fn set_key_value_raw( let m: KeyValue = stmt .query_row(&*params.as_params(), |row| row.try_into()) .expect("Failed to upsert KeyValue"); - emit_upserted_model(w, &AnyModel::KeyValue(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::KeyValue(m.to_owned()), update_source); (m, existing.is_none()) } pub async fn delete_key_value( - w: &WebviewWindow, + app_handle: &AppHandle, namespace: &str, key: &str, update_source: &UpdateSource, ) { - let kv = match get_key_value_raw(w, namespace, key).await { + let kv = match get_key_value_raw(app_handle, namespace, key).await { None => return, Some(m) => m, }; - let dbm = &*w.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() .from_table(KeyValueIden::Table) @@ -156,11 +156,11 @@ pub async fn delete_key_value( ) .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params()).expect("Failed to delete PluginKeyValue"); - emit_deleted_model(w, &AnyModel::KeyValue(kv.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::KeyValue(kv.to_owned()), update_source); } -pub async fn list_key_values_raw(mgr: &impl Manager) -> Result> { - let dbm = &*mgr.state::(); +pub async fn list_key_values_raw(app_handle: &AppHandle) -> Result> { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(KeyValueIden::Table) @@ -172,11 +172,11 @@ pub async fn list_key_values_raw(mgr: &impl Manager) -> Result( - mgr: &impl Manager, + app_handle: &AppHandle, namespace: &str, key: &str, ) -> Option { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(KeyValueIden::Table) @@ -192,11 +192,11 @@ pub async fn get_key_value_raw( } pub async fn get_plugin_key_value( - mgr: &impl Manager, + app_handle: &AppHandle, plugin_name: &str, key: &str, ) -> Option { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(PluginKeyValueIden::Table) @@ -212,14 +212,14 @@ pub async fn get_plugin_key_value( } pub async fn set_plugin_key_value( - mgr: &impl Manager, + app_handle: &AppHandle, plugin_name: &str, key: &str, value: &str, ) -> (PluginKeyValue, bool) { - let existing = get_plugin_key_value(mgr, plugin_name, key).await; + let existing = get_plugin_key_value(app_handle, plugin_name, key).await; - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(PluginKeyValueIden::Table) @@ -253,15 +253,15 @@ pub async fn set_plugin_key_value( } pub async fn delete_plugin_key_value( - mgr: &impl Manager, + app_handle: &AppHandle, plugin_name: &str, key: &str, ) -> bool { - if let None = get_plugin_key_value(mgr, plugin_name, key).await { + if let None = get_plugin_key_value(app_handle, plugin_name, key).await { return false; } - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() .from_table(PluginKeyValueIden::Table) @@ -271,12 +271,13 @@ pub async fn delete_plugin_key_value( .add(Expr::col(PluginKeyValueIden::Key).eq(key)), ) .build_rusqlite(SqliteQueryBuilder); - db.execute(sql.as_str(), &*params.as_params()).expect("Failed to delete PluginKeyValue"); + let mut stmt = db.prepare(sql.as_str()).unwrap(); + stmt.execute(&*params.as_params()).expect("Failed to delete PluginKeyValue"); true } -pub async fn list_workspaces(mgr: &impl Manager) -> Result> { - let dbm = &*mgr.state::(); +pub async fn list_workspaces(app_handle: &AppHandle) -> Result> { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WorkspaceIden::Table) @@ -288,8 +289,10 @@ pub async fn list_workspaces(mgr: &impl Manager) -> Result(mgr: &impl Manager) -> Result> { - let dbm = &*mgr.state::(); +pub async fn list_workspace_metas( + app_handle: &AppHandle, +) -> Result> { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WorkspaceMetaIden::Table) @@ -300,8 +303,8 @@ pub async fn list_workspace_metas(mgr: &impl Manager) -> Result(mgr: &impl Manager, id: &str) -> Result { - let dbm = &*mgr.state::(); +pub async fn get_workspace(app_handle: &AppHandle, id: &str) -> Result { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WorkspaceIden::Table) @@ -313,10 +316,10 @@ pub async fn get_workspace(mgr: &impl Manager, id: &str) -> Resul } pub async fn get_workspace_meta( - mgr: &impl Manager, + app_handle: &AppHandle, workspace: &Workspace, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WorkspaceMetaIden::Table) @@ -328,17 +331,17 @@ pub async fn get_workspace_meta( } pub async fn get_or_create_workspace_meta( - window: &WebviewWindow, + app_handle: &AppHandle, workspace: &Workspace, update_source: &UpdateSource, ) -> Result { - let workspace_meta = get_workspace_meta(window, workspace).await?; + let workspace_meta = get_workspace_meta(app_handle, workspace).await?; if let Some(m) = workspace_meta { return Ok(m); } upsert_workspace_meta( - window, + app_handle, WorkspaceMeta { workspace_id: workspace.to_owned().id, ..Default::default() @@ -348,8 +351,8 @@ pub async fn get_or_create_workspace_meta( .await } -pub async fn exists_workspace(mgr: &impl Manager, id: &str) -> Result { - let dbm = &*mgr.state::(); +pub async fn exists_workspace(app_handle: &AppHandle, id: &str) -> Result { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WorkspaceIden::Table) @@ -361,7 +364,7 @@ pub async fn exists_workspace(mgr: &impl Manager, id: &str) -> Re } pub async fn upsert_workspace( - window: &WebviewWindow, + app_handle: &AppHandle, workspace: Workspace, update_source: &UpdateSource, ) -> Result { @@ -371,7 +374,7 @@ pub async fn upsert_workspace( }; let trimmed_name = workspace.name.trim(); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -415,12 +418,12 @@ pub async fn upsert_workspace( let mut stmt = db.prepare(&sql)?; let m: Workspace = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::Workspace(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::Workspace(m.to_owned()), update_source); Ok(m) } pub async fn upsert_workspace_meta( - window: &WebviewWindow, + app_handle: &AppHandle, workspace_meta: WorkspaceMeta, update_source: &UpdateSource, ) -> Result { @@ -429,7 +432,7 @@ pub async fn upsert_workspace_meta( _ => workspace_meta.id.to_string(), }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -461,18 +464,18 @@ pub async fn upsert_workspace_meta( let mut stmt = db.prepare(&sql)?; let m: WorkspaceMeta = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::WorkspaceMeta(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::WorkspaceMeta(m.to_owned()), update_source); Ok(m) } pub async fn delete_workspace( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let workspace = get_workspace(window, id).await?; + let workspace = get_workspace(app_handle, id).await?; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() @@ -481,16 +484,16 @@ pub async fn delete_workspace( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - for r in list_responses_by_workspace_id(window, id).await? { - delete_http_response(window, &r.id, update_source).await?; + for r in list_responses_by_workspace_id(app_handle, id).await? { + delete_http_response(app_handle, &r.id, update_source).await?; } - emit_deleted_model(window, &AnyModel::Workspace(workspace.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::Workspace(workspace.to_owned()), update_source); Ok(workspace) } -pub async fn get_cookie_jar(mgr: &impl Manager, id: &str) -> Result { - let dbm = &*mgr.state::(); +pub async fn get_cookie_jar(app_handle: &AppHandle, id: &str) -> Result { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -503,10 +506,10 @@ pub async fn get_cookie_jar(mgr: &impl Manager, id: &str) -> Resu } pub async fn list_cookie_jars( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(CookieJarIden::Table) @@ -519,12 +522,12 @@ pub async fn list_cookie_jars( } pub async fn delete_cookie_jar( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let cookie_jar = get_cookie_jar(window, id).await?; - let dbm = &*window.app_handle().state::(); + let cookie_jar = get_cookie_jar(app_handle, id).await?; + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() @@ -533,16 +536,16 @@ pub async fn delete_cookie_jar( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::CookieJar(cookie_jar.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::CookieJar(cookie_jar.to_owned()), update_source); Ok(cookie_jar) } pub async fn duplicate_grpc_request( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let mut request = match get_grpc_request(window, id).await? { + let mut request = match get_grpc_request(app_handle, id).await? { Some(r) => r, None => { return Err(ModelNotFound(id.to_string())); @@ -550,22 +553,22 @@ pub async fn duplicate_grpc_request( }; request.sort_priority = request.sort_priority + 0.001; request.id = "".to_string(); - upsert_grpc_request(window, request, update_source).await + upsert_grpc_request(app_handle, request, update_source).await } pub async fn delete_grpc_request( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let grpc_request = match get_grpc_request(window, id).await? { + let grpc_request = match get_grpc_request(app_handle, id).await? { Some(r) => r, None => { return Err(ModelNotFound(id.to_string())); } }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() .from_table(GrpcRequestIden::Table) @@ -573,12 +576,12 @@ pub async fn delete_grpc_request( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::GrpcRequest(grpc_request.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::GrpcRequest(grpc_request.to_owned()), update_source); Ok(grpc_request) } pub async fn upsert_grpc_request( - window: &WebviewWindow, + app_handle: &AppHandle, request: GrpcRequest, update_source: &UpdateSource, ) -> Result { @@ -588,7 +591,7 @@ pub async fn upsert_grpc_request( }; let trimmed_name = request.name.trim(); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(GrpcRequestIden::Table) @@ -650,15 +653,15 @@ pub async fn upsert_grpc_request( let mut stmt = db.prepare(sql.as_str())?; let m: GrpcRequest = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::GrpcRequest(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::GrpcRequest(m.to_owned()), update_source); Ok(m) } pub async fn get_grpc_request( - mgr: &impl Manager, + app_handle: &AppHandle, id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -671,10 +674,10 @@ pub async fn get_grpc_request( } pub async fn list_grpc_requests( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(GrpcRequestIden::Table) @@ -687,22 +690,22 @@ pub async fn list_grpc_requests( } pub async fn upsert_grpc_connection( - window: &WebviewWindow, + app_handle: &AppHandle, connection: &GrpcConnection, update_source: &UpdateSource, ) -> Result { let connections = - list_grpc_connections_for_request(window, connection.request_id.as_str()).await?; + list_grpc_connections_for_request(app_handle, connection.request_id.as_str()).await?; for c in connections.iter().skip(MAX_HISTORY_ITEMS - 1) { debug!("Deleting old grpc connection {}", c.id); - delete_grpc_connection(window, c.id.as_str(), update_source).await?; + delete_grpc_connection(app_handle, c.id.as_str(), update_source).await?; } let id = match connection.id.as_str() { "" => generate_model_id(ModelType::TypeGrpcConnection), _ => connection.id.to_string(), }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(GrpcConnectionIden::Table) @@ -756,15 +759,15 @@ pub async fn upsert_grpc_connection( let mut stmt = db.prepare(sql.as_str())?; let m: GrpcConnection = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::GrpcConnection(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::GrpcConnection(m.to_owned()), update_source); Ok(m) } pub async fn get_grpc_connection( - mgr: &impl Manager, + app_handle: &AppHandle, id: &str, ) -> Result { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(GrpcConnectionIden::Table) @@ -776,10 +779,10 @@ pub async fn get_grpc_connection( } pub async fn list_grpc_connections_for_workspace( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -794,10 +797,10 @@ pub async fn list_grpc_connections_for_workspace( } pub async fn list_grpc_connections_for_request( - mgr: &impl Manager, + app_handle: &AppHandle, request_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -812,13 +815,13 @@ pub async fn list_grpc_connections_for_request( } pub async fn delete_grpc_connection( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let m = get_grpc_connection(window, id).await?; + let m = get_grpc_connection(app_handle, id).await?; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() @@ -827,34 +830,34 @@ pub async fn delete_grpc_connection( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::GrpcConnection(m.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::GrpcConnection(m.to_owned()), update_source); Ok(m) } pub async fn delete_all_grpc_connections( - window: &WebviewWindow, + app_handle: &AppHandle, request_id: &str, update_source: &UpdateSource, ) -> Result<()> { - for r in list_grpc_connections_for_request(window, request_id).await? { - delete_grpc_connection(window, &r.id, update_source).await?; + for r in list_grpc_connections_for_request(app_handle, request_id).await? { + delete_grpc_connection(app_handle, &r.id, update_source).await?; } Ok(()) } pub async fn delete_all_grpc_connections_for_workspace( - window: &WebviewWindow, + app_handle: &AppHandle, workspace_id: &str, update_source: &UpdateSource, ) -> Result<()> { - for r in list_grpc_connections_for_workspace(window, workspace_id).await? { - delete_grpc_connection(window, &r.id, update_source).await?; + for r in list_grpc_connections_for_workspace(app_handle, workspace_id).await? { + delete_grpc_connection(app_handle, &r.id, update_source).await?; } Ok(()) } pub async fn upsert_grpc_event( - window: &WebviewWindow, + app_handle: &AppHandle, event: &GrpcEvent, update_source: &UpdateSource, ) -> Result { @@ -863,7 +866,7 @@ pub async fn upsert_grpc_event( _ => event.id.to_string(), }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(GrpcEventIden::Table) @@ -910,12 +913,12 @@ pub async fn upsert_grpc_event( let mut stmt = db.prepare(sql.as_str())?; let m: GrpcEvent = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::GrpcEvent(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::GrpcEvent(m.to_owned()), update_source); Ok(m) } -pub async fn get_grpc_event(mgr: &impl Manager, id: &str) -> Result { - let dbm = &*mgr.state::(); +pub async fn get_grpc_event(app_handle: &AppHandle, id: &str) -> Result { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(GrpcEventIden::Table) @@ -927,10 +930,10 @@ pub async fn get_grpc_event(mgr: &impl Manager, id: &str) -> Resu } pub async fn list_grpc_events( - mgr: &impl Manager, + app_handle: &AppHandle, connection_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -945,18 +948,18 @@ pub async fn list_grpc_events( } pub async fn delete_websocket_request( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let request = match get_websocket_request(window, id).await? { + let request = match get_websocket_request(app_handle, id).await? { Some(r) => r, None => { return Err(ModelNotFound(id.to_string())); } }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() .from_table(WebsocketRequestIden::Table) @@ -964,18 +967,18 @@ pub async fn delete_websocket_request( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::WebsocketRequest(request.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::WebsocketRequest(request.to_owned()), update_source); Ok(request) } pub async fn delete_websocket_connection( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let m = get_websocket_connection(window, id).await?; + let m = get_websocket_connection(app_handle, id).await?; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() .from_table(WebsocketConnectionIden::Table) @@ -983,37 +986,37 @@ pub async fn delete_websocket_connection( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::WebsocketConnection(m.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::WebsocketConnection(m.to_owned()), update_source); Ok(m) } pub async fn delete_all_websocket_connections( - window: &WebviewWindow, + app_handle: &AppHandle, request_id: &str, update_source: &UpdateSource, ) -> Result<()> { - for c in list_websocket_connections_for_request(window, request_id).await? { - delete_websocket_connection(window, &c.id, update_source).await?; + for c in list_websocket_connections_for_request(app_handle, request_id).await? { + delete_websocket_connection(app_handle, &c.id, update_source).await?; } Ok(()) } pub async fn delete_all_websocket_connections_for_workspace( - window: &WebviewWindow, + app_handle: &AppHandle, workspace_id: &str, update_source: &UpdateSource, ) -> Result<()> { - for c in list_websocket_connections_for_workspace(window, workspace_id).await? { - delete_websocket_connection(window, &c.id, update_source).await?; + for c in list_websocket_connections_for_workspace(app_handle, workspace_id).await? { + delete_websocket_connection(app_handle, &c.id, update_source).await?; } Ok(()) } pub async fn get_websocket_connection( - mgr: &impl Manager, + app_handle: &AppHandle, id: &str, ) -> Result { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WebsocketConnectionIden::Table) @@ -1025,7 +1028,7 @@ pub async fn get_websocket_connection( } pub async fn upsert_websocket_event( - window: &WebviewWindow, + app_handle: &AppHandle, event: WebsocketEvent, update_source: &UpdateSource, ) -> Result { @@ -1034,7 +1037,7 @@ pub async fn upsert_websocket_event( _ => event.id.to_string(), }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(WebsocketEventIden::Table) @@ -1075,26 +1078,26 @@ pub async fn upsert_websocket_event( let mut stmt = db.prepare(sql.as_str())?; let m: WebsocketEvent = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::WebsocketEvent(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::WebsocketEvent(m.to_owned()), update_source); Ok(m) } pub async fn duplicate_websocket_request( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let mut request = match get_websocket_request(window, id).await? { + let mut request = match get_websocket_request(app_handle, id).await? { None => return Err(ModelNotFound(id.to_string())), Some(r) => r, }; request.id = "".to_string(); request.sort_priority = request.sort_priority + 0.001; - upsert_websocket_request(window, request, update_source).await + upsert_websocket_request(app_handle, request, update_source).await } pub async fn upsert_websocket_request( - window: &WebviewWindow, + app_handle: &AppHandle, request: WebsocketRequest, update_source: &UpdateSource, ) -> Result { @@ -1104,7 +1107,7 @@ pub async fn upsert_websocket_request( }; let trimmed_name = request.name.trim(); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(WebsocketRequestIden::Table) @@ -1163,15 +1166,15 @@ pub async fn upsert_websocket_request( let mut stmt = db.prepare(sql.as_str())?; let m: WebsocketRequest = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::WebsocketRequest(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::WebsocketRequest(m.to_owned()), update_source); Ok(m) } pub async fn list_websocket_connections_for_workspace( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1186,10 +1189,10 @@ pub async fn list_websocket_connections_for_workspace( } pub async fn list_websocket_connections_for_request( - mgr: &impl Manager, + app_handle: &AppHandle, request_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1204,22 +1207,22 @@ pub async fn list_websocket_connections_for_request( } pub async fn upsert_websocket_connection( - window: &WebviewWindow, + app_handle: &AppHandle, connection: &WebsocketConnection, update_source: &UpdateSource, ) -> Result { let connections = - list_websocket_connections_for_request(window, connection.request_id.as_str()).await?; + list_websocket_connections_for_request(app_handle, connection.request_id.as_str()).await?; for c in connections.iter().skip(MAX_HISTORY_ITEMS - 1) { debug!("Deleting old websocket connection {}", c.id); - delete_websocket_connection(window, c.id.as_str(), update_source).await?; + delete_websocket_connection(app_handle, c.id.as_str(), update_source).await?; } let id = match connection.id.as_str() { "" => generate_model_id(ModelType::TypeWebSocketConnection), _ => connection.id.to_string(), }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() .into_table(WebsocketConnectionIden::Table) @@ -1267,15 +1270,15 @@ pub async fn upsert_websocket_connection( let mut stmt = db.prepare(sql.as_str())?; let m: WebsocketConnection = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::WebsocketConnection(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::WebsocketConnection(m.to_owned()), update_source); Ok(m) } pub async fn get_websocket_request( - mgr: &impl Manager, + app_handle: &AppHandle, id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1288,10 +1291,10 @@ pub async fn get_websocket_request( } pub async fn list_websocket_requests( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WebsocketRequestIden::Table) @@ -1304,10 +1307,10 @@ pub async fn list_websocket_requests( } pub async fn list_websocket_events( - mgr: &impl Manager, + app_handle: &AppHandle, connection_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(WebsocketEventIden::Table) @@ -1320,7 +1323,7 @@ pub async fn list_websocket_events( } pub async fn upsert_cookie_jar( - window: &WebviewWindow, + app_handle: &AppHandle, cookie_jar: &CookieJar, update_source: &UpdateSource, ) -> Result { @@ -1330,7 +1333,7 @@ pub async fn upsert_cookie_jar( }; let trimmed_name = cookie_jar.name.trim(); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -1365,22 +1368,22 @@ pub async fn upsert_cookie_jar( let mut stmt = db.prepare(sql.as_str())?; let m: CookieJar = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::CookieJar(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::CookieJar(m.to_owned()), update_source); Ok(m) } pub async fn ensure_base_environment( - window: &WebviewWindow, + app_handle: &AppHandle, workspace_id: &str, ) -> Result<()> { - let environments = list_environments(window, workspace_id).await?; + let environments = list_environments(app_handle, workspace_id).await?; let base_environment = environments.iter().find(|e| e.environment_id == None && e.workspace_id == workspace_id); if let None = base_environment { info!("Creating base environment for {workspace_id}"); upsert_environment( - window, + app_handle, Environment { workspace_id: workspace_id.to_string(), name: "Global Variables".to_string(), @@ -1395,11 +1398,11 @@ pub async fn ensure_base_environment( } pub async fn list_environments( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { let environments: Vec = { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(EnvironmentIden::Table) @@ -1416,13 +1419,13 @@ pub async fn list_environments( } pub async fn delete_environment( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let env = get_environment(window, id).await?; + let env = get_environment(app_handle, id).await?; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() @@ -1432,14 +1435,14 @@ pub async fn delete_environment( db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::Environment(env.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::Environment(env.to_owned()), update_source); Ok(env) } const SETTINGS_ID: &str = "default"; -async fn get_settings(mgr: &impl Manager) -> Result> { - let dbm = &*mgr.state::(); +async fn get_settings(app_handle: &AppHandle) -> Result> { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1451,14 +1454,14 @@ async fn get_settings(mgr: &impl Manager) -> Result(mgr: &impl Manager) -> Settings { - match get_settings(mgr).await { +pub async fn get_or_create_settings(app_handle: &AppHandle) -> Settings { + match get_settings(app_handle).await { Ok(Some(settings)) => return settings, Err(e) => panic!("Failed to get settings {e:?}"), Ok(None) => {} }; - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -1473,7 +1476,7 @@ pub async fn get_or_create_settings(mgr: &impl Manager) -> Settin } pub async fn update_settings( - window: &WebviewWindow, + app_handle: &AppHandle, settings: Settings, update_source: &UpdateSource, ) -> Result { @@ -1484,7 +1487,7 @@ pub async fn update_settings( settings.created_at }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::update() @@ -1518,12 +1521,12 @@ pub async fn update_settings( let mut stmt = db.prepare(sql.as_str())?; let m: Settings = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::Settings(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::Settings(m.to_owned()), update_source); Ok(m) } pub async fn upsert_environment( - window: &WebviewWindow, + app_handle: &AppHandle, environment: Environment, update_source: &UpdateSource, ) -> Result { @@ -1533,7 +1536,7 @@ pub async fn upsert_environment( }; let trimmed_name = environment.name.trim(); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -1570,12 +1573,15 @@ pub async fn upsert_environment( let mut stmt = db.prepare(sql.as_str())?; let m: Environment = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::Environment(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::Environment(m.to_owned()), update_source); Ok(m) } -pub async fn get_environment(mgr: &impl Manager, id: &str) -> Result { - let dbm = &*mgr.state::(); +pub async fn get_environment( + app_handle: &AppHandle, + id: &str, +) -> Result { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1588,10 +1594,10 @@ pub async fn get_environment(mgr: &impl Manager, id: &str) -> Res } pub async fn get_base_environment( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1607,8 +1613,8 @@ pub async fn get_base_environment( Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?) } -pub async fn get_plugin(mgr: &impl Manager, id: &str) -> Result { - let dbm = &*mgr.state::(); +pub async fn get_plugin(app_handle: &AppHandle, id: &str) -> Result { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1620,8 +1626,8 @@ pub async fn get_plugin(mgr: &impl Manager, id: &str) -> Result

(mgr: &impl Manager) -> Result> { - let dbm = &*mgr.state::(); +pub async fn list_plugins(app_handle: &AppHandle) -> Result> { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1635,7 +1641,7 @@ pub async fn list_plugins(mgr: &impl Manager) -> Result( - window: &WebviewWindow, + app_handle: &AppHandle, plugin: Plugin, update_source: &UpdateSource, ) -> Result { @@ -1643,7 +1649,7 @@ pub async fn upsert_plugin( "" => generate_model_id(ModelType::TypePlugin), _ => plugin.id.to_string(), }; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -1682,19 +1688,19 @@ pub async fn upsert_plugin( let mut stmt = db.prepare(sql.as_str())?; let m: Plugin = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::Plugin(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::Plugin(m.to_owned()), update_source); Ok(m) } pub async fn delete_plugin( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let plugin = get_plugin(window, id).await?; + let plugin = get_plugin(app_handle, id).await?; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() @@ -1703,12 +1709,12 @@ pub async fn delete_plugin( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::Plugin(plugin.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::Plugin(plugin.to_owned()), update_source); Ok(plugin) } -pub async fn get_folder(mgr: &impl Manager, id: &str) -> Result { - let dbm = &*mgr.state::(); +pub async fn get_folder(app_handle: &AppHandle, id: &str) -> Result { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1721,10 +1727,10 @@ pub async fn get_folder(mgr: &impl Manager, id: &str) -> Result( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -1739,14 +1745,14 @@ pub async fn list_folders( } pub async fn delete_folder( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let folder = get_folder(window, id).await?; + let folder = get_folder(app_handle, id).await?; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() @@ -1755,12 +1761,12 @@ pub async fn delete_folder( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::Folder(folder.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::Folder(folder.to_owned()), update_source); Ok(folder) } pub async fn upsert_folder( - window: &WebviewWindow, + app_handle: &AppHandle, folder: Folder, update_source: &UpdateSource, @@ -1771,7 +1777,7 @@ pub async fn upsert_folder( }; let trimmed_name = folder.name.trim(); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -1812,90 +1818,92 @@ pub async fn upsert_folder( let mut stmt = db.prepare(sql.as_str())?; let m: Folder = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::Folder(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::Folder(m.to_owned()), update_source); Ok(m) } pub async fn duplicate_http_request( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let mut request = match get_http_request(window, id).await? { + let mut request = match get_http_request(app_handle, id).await? { None => return Err(ModelNotFound(id.to_string())), Some(r) => r, }; request.id = "".to_string(); request.sort_priority = request.sort_priority + 0.001; - upsert_http_request(window, request, update_source).await + upsert_http_request(app_handle, request, update_source).await } pub async fn duplicate_folder( - window: &WebviewWindow, + app_handle: &AppHandle, src_folder: &Folder, + update_source: &UpdateSource, ) -> Result<()> { let workspace_id = src_folder.workspace_id.as_str(); - let http_requests = list_http_requests(window, workspace_id) + let http_requests = list_http_requests(app_handle, workspace_id) .await? .into_iter() .filter(|m| m.folder_id.as_ref() == Some(&src_folder.id)); - let grpc_requests = list_grpc_requests(window, workspace_id) + let grpc_requests = list_grpc_requests(app_handle, workspace_id) .await? .into_iter() .filter(|m| m.folder_id.as_ref() == Some(&src_folder.id)); - let folders = list_folders(window, workspace_id) + let folders = list_folders(app_handle, workspace_id) .await? .into_iter() .filter(|m| m.folder_id.as_ref() == Some(&src_folder.id)); let new_folder = upsert_folder( - window, + app_handle, Folder { id: "".into(), sort_priority: src_folder.sort_priority + 0.001, ..src_folder.clone() }, - &UpdateSource::Window, + update_source, ) .await?; for m in http_requests { upsert_http_request( - window, + app_handle, HttpRequest { id: "".into(), folder_id: Some(new_folder.id.clone()), sort_priority: m.sort_priority + 0.001, ..m }, - &UpdateSource::Window, + update_source, ) .await?; } for m in grpc_requests { upsert_grpc_request( - window, + app_handle, GrpcRequest { id: "".into(), folder_id: Some(new_folder.id.clone()), sort_priority: m.sort_priority + 0.001, ..m }, - &UpdateSource::Window, + update_source, ) .await?; } for m in folders { // Recurse down Box::pin(duplicate_folder( - window, + app_handle, &Folder { folder_id: Some(new_folder.id.clone()), ..m }, + update_source, )) .await?; } @@ -1903,7 +1911,7 @@ pub async fn duplicate_folder( } pub async fn upsert_http_request( - window: &WebviewWindow, + app_handle: &AppHandle, request: HttpRequest, update_source: &UpdateSource, ) -> Result { @@ -1913,7 +1921,7 @@ pub async fn upsert_http_request( }; let trimmed_name = request.name.trim(); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -1979,15 +1987,15 @@ pub async fn upsert_http_request( let mut stmt = db.prepare(sql.as_str())?; let m: HttpRequest = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::HttpRequest(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::HttpRequest(m.to_owned()), update_source); Ok(m) } pub async fn list_http_requests( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(HttpRequestIden::Table) @@ -2001,10 +2009,10 @@ pub async fn list_http_requests( } pub async fn get_http_request( - mgr: &impl Manager, + app_handle: &AppHandle, id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() @@ -2017,19 +2025,19 @@ pub async fn get_http_request( } pub async fn delete_http_request( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let req = match get_http_request(window, id).await? { + let req = match get_http_request(app_handle, id).await? { None => return Err(ModelNotFound(id.to_string())), Some(r) => r, }; // DB deletes will cascade but this will delete the files - delete_all_http_responses_for_request(window, id, update_source).await?; + delete_all_http_responses_for_request(app_handle, id, update_source).await?; - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() .from_table(HttpRequestIden::Table) @@ -2037,17 +2045,17 @@ pub async fn delete_http_request( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::HttpRequest(req.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::HttpRequest(req.to_owned()), update_source); Ok(req) } pub async fn create_default_http_response( - window: &WebviewWindow, + app_handle: &AppHandle, request_id: &str, update_source: &UpdateSource, ) -> Result { create_http_response( - &window, + &app_handle, request_id, 0, 0, @@ -2067,7 +2075,7 @@ pub async fn create_default_http_response( #[allow(clippy::too_many_arguments)] pub async fn create_http_response( - window: &WebviewWindow, + app_handle: &AppHandle, request_id: &str, elapsed: i64, elapsed_headers: i64, @@ -2082,18 +2090,18 @@ pub async fn create_http_response( remote_addr: Option<&str>, update_source: &UpdateSource, ) -> Result { - let responses = list_http_responses_for_request(window, request_id, None).await?; + let responses = list_http_responses_for_request(app_handle, request_id, None).await?; for response in responses.iter().skip(MAX_HISTORY_ITEMS - 1) { debug!("Deleting old response {}", response.id); - delete_http_response(window, response.id.as_str(), update_source).await?; + delete_http_response(app_handle, response.id.as_str(), update_source).await?; } - let req = match get_http_request(window, request_id).await? { + let req = match get_http_request(app_handle, request_id).await? { None => return Err(ModelNotFound(request_id.to_string())), Some(r) => r, }; let id = generate_model_id(ModelType::TypeHttpResponse); - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -2139,12 +2147,14 @@ pub async fn create_http_response( let mut stmt = db.prepare(sql.as_str())?; let m: HttpResponse = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::HttpResponse(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::HttpResponse(m.to_owned()), update_source); Ok(m) } -pub async fn cancel_pending_websocket_connections(mgr: &impl Manager) -> Result<()> { - let dbm = &*mgr.state::(); +pub async fn cancel_pending_websocket_connections( + app_handle: &AppHandle, +) -> Result<()> { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let closed = serde_json::to_value(&WebsocketConnectionState::Closed)?; @@ -2192,23 +2202,23 @@ pub async fn cancel_pending_http_responses(app: &AppHandle) -> Result<()> { } pub async fn update_response_if_id( - window: &WebviewWindow, + app_handle: &AppHandle, response: &HttpResponse, update_source: &UpdateSource, ) -> Result { if response.id.is_empty() { Ok(response.clone()) } else { - update_http_response(window, response, update_source).await + update_http_response(app_handle, response, update_source).await } } pub async fn update_http_response( - window: &WebviewWindow, + app_handle: &AppHandle, response: &HttpResponse, update_source: &UpdateSource, ) -> Result { - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::update() @@ -2242,15 +2252,15 @@ pub async fn update_http_response( let mut stmt = db.prepare(sql.as_str())?; let m: HttpResponse = stmt.query_row(&*params.as_params(), |row| row.try_into())?; - emit_upserted_model(window, &AnyModel::HttpResponse(m.to_owned()), update_source); + emit_upserted_model(app_handle, &AnyModel::HttpResponse(m.to_owned()), update_source); Ok(m) } pub async fn get_http_response( - mgr: &impl Manager, + app_handle: &AppHandle, id: &str, ) -> Result { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(HttpResponseIden::Table) @@ -2262,11 +2272,11 @@ pub async fn get_http_response( } pub async fn delete_http_response( - window: &WebviewWindow, + app_handle: &AppHandle, id: &str, update_source: &UpdateSource, ) -> Result { - let resp = get_http_response(window, id).await?; + let resp = get_http_response(app_handle, id).await?; // Delete the body file if it exists if let Some(p) = resp.body_path.clone() { @@ -2275,7 +2285,7 @@ pub async fn delete_http_response( }; } - let dbm = &*window.app_handle().state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() .from_table(HttpResponseIden::Table) @@ -2283,39 +2293,39 @@ pub async fn delete_http_response( .build_rusqlite(SqliteQueryBuilder); db.execute(sql.as_str(), &*params.as_params())?; - emit_deleted_model(window, &AnyModel::HttpResponse(resp.to_owned()), update_source); + emit_deleted_model(app_handle, &AnyModel::HttpResponse(resp.to_owned()), update_source); Ok(resp) } pub async fn delete_all_http_responses_for_request( - window: &WebviewWindow, + app_handle: &AppHandle, request_id: &str, update_source: &UpdateSource, ) -> Result<()> { - for r in list_http_responses_for_request(window, request_id, None).await? { - delete_http_response(window, &r.id, update_source).await?; + for r in list_http_responses_for_request(app_handle, request_id, None).await? { + delete_http_response(app_handle, &r.id, update_source).await?; } Ok(()) } pub async fn delete_all_http_responses_for_workspace( - window: &WebviewWindow, + app_handle: &AppHandle, workspace_id: &str, update_source: &UpdateSource, ) -> Result<()> { - for r in list_http_responses_for_workspace(window, workspace_id, None).await? { - delete_http_response(window, &r.id, update_source).await?; + for r in list_http_responses_for_workspace(app_handle, workspace_id, None).await? { + delete_http_response(app_handle, &r.id, update_source).await?; } Ok(()) } pub async fn list_http_responses_for_workspace( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, limit: Option, ) -> Result> { let limit_unwrapped = limit.unwrap_or_else(|| i64::MAX); - let dbm = mgr.state::(); + let dbm = app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(HttpResponseIden::Table) @@ -2330,12 +2340,12 @@ pub async fn list_http_responses_for_workspace( } pub async fn list_http_responses_for_request( - mgr: &impl Manager, + app_handle: &AppHandle, request_id: &str, limit: Option, ) -> Result> { let limit_unwrapped = limit.unwrap_or_else(|| i64::MAX); - let dbm = mgr.state::(); + let dbm = app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(HttpResponseIden::Table) @@ -2350,10 +2360,10 @@ pub async fn list_http_responses_for_request( } pub async fn list_responses_by_workspace_id( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(HttpResponseIden::Table) @@ -2367,11 +2377,11 @@ pub async fn list_responses_by_workspace_id( } pub async fn get_sync_state_for_model( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, model_id: &str, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(SyncStateIden::Table) @@ -2387,11 +2397,11 @@ pub async fn get_sync_state_for_model( } pub async fn list_sync_states_for_workspace( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, sync_dir: &Path, ) -> Result> { - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::select() .from(SyncStateIden::Table) @@ -2408,7 +2418,7 @@ pub async fn list_sync_states_for_workspace( } pub async fn upsert_sync_state( - mgr: &impl Manager, + app_handle: &AppHandle, sync_state: SyncState, ) -> Result { let id = match sync_state.id.as_str() { @@ -2416,7 +2426,7 @@ pub async fn upsert_sync_state( _ => sync_state.id.to_string(), }; - let dbm = &*mgr.state::(); + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::insert() @@ -2462,8 +2472,8 @@ pub async fn upsert_sync_state( Ok(m) } -pub async fn delete_sync_state(mgr: &impl Manager, id: &str) -> Result<()> { - let dbm = &*mgr.app_handle().state::(); +pub async fn delete_sync_state(app_handle: &AppHandle, id: &str) -> Result<()> { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await.get().unwrap(); let (sql, params) = Query::delete() @@ -2474,8 +2484,8 @@ pub async fn delete_sync_state(mgr: &impl Manager, id: &str) -> R Ok(()) } -pub async fn debug_pool(mgr: &impl Manager) { - let dbm = &*mgr.state::(); +pub async fn debug_pool(app_handle: &AppHandle) { + let dbm = &*app_handle.state::(); let db = dbm.0.lock().await; debug!("Debug database state: {:?}", db.state()); } @@ -2501,46 +2511,51 @@ pub fn generate_id() -> String { #[ts(export, export_to = "gen_models.ts")] pub struct ModelPayload { pub model: AnyModel, - pub window_label: String, pub update_source: UpdateSource, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[serde(rename_all = "snake_case")] +#[serde(rename_all = "snake_case", tag = "type")] #[ts(export, export_to = "gen_models.ts")] pub enum UpdateSource { Sync, - Window, + Window { label: String }, Plugin, Background, Import, } +impl UpdateSource { + pub fn from_window(window: &WebviewWindow) -> Self { + Self::Window { + label: window.label().to_string(), + } + } +} + fn emit_upserted_model( - window: &WebviewWindow, + app_handle: &AppHandle, model: &AnyModel, update_source: &UpdateSource, ) { let payload = ModelPayload { model: model.to_owned(), - window_label: window.label().to_string(), update_source: update_source.to_owned(), }; - window.emit("upserted_model", payload).unwrap(); + app_handle.emit("upserted_model", payload).unwrap(); } fn emit_deleted_model( - window: &WebviewWindow, + app_handle: &AppHandle, model: &AnyModel, update_source: &UpdateSource, ) { let payload = ModelPayload { model: model.to_owned(), - window_label: window.label().to_string(), update_source: update_source.to_owned(), }; - window.emit("deleted_model", payload).unwrap(); + app_handle.emit("deleted_model", payload).unwrap(); } pub fn listen_to_model_delete(app_handle: &AppHandle, handler: F) @@ -2596,7 +2611,7 @@ pub struct BatchUpsertResult { } pub async fn batch_upsert( - window: &WebviewWindow, + app_handle: &AppHandle, workspaces: Vec, environments: Vec, folders: Vec, @@ -2610,7 +2625,7 @@ pub async fn batch_upsert( if workspaces.len() > 0 { info!("Batch inserting {} workspaces", workspaces.len()); for v in workspaces { - let x = upsert_workspace(&window, v, update_source).await?; + let x = upsert_workspace(&app_handle, v, update_source).await?; imported_resources.workspaces.push(x.clone()); } } @@ -2630,7 +2645,7 @@ pub async fn batch_upsert( if let Some(_) = imported_resources.environments.iter().find(|f| f.id == v.id) { continue; } - let x = upsert_environment(&window, v, update_source).await?; + let x = upsert_environment(&app_handle, v, update_source).await?; imported_resources.environments.push(x.clone()); } } @@ -2651,7 +2666,7 @@ pub async fn batch_upsert( if let Some(_) = imported_resources.folders.iter().find(|f| f.id == v.id) { continue; } - let x = upsert_folder(&window, v, update_source).await?; + let x = upsert_folder(&app_handle, v, update_source).await?; imported_resources.folders.push(x.clone()); } } @@ -2660,7 +2675,7 @@ pub async fn batch_upsert( if http_requests.len() > 0 { for v in http_requests { - let x = upsert_http_request(&window, v, update_source).await?; + let x = upsert_http_request(&app_handle, v, update_source).await?; imported_resources.http_requests.push(x.clone()); } info!("Imported {} http_requests", imported_resources.http_requests.len()); @@ -2668,7 +2683,7 @@ pub async fn batch_upsert( if grpc_requests.len() > 0 { for v in grpc_requests { - let x = upsert_grpc_request(&window, v, update_source).await?; + let x = upsert_grpc_request(&app_handle, v, update_source).await?; imported_resources.grpc_requests.push(x.clone()); } info!("Imported {} grpc_requests", imported_resources.grpc_requests.len()); @@ -2676,7 +2691,7 @@ pub async fn batch_upsert( if websocket_requests.len() > 0 { for v in websocket_requests { - let x = upsert_websocket_request(&window, v, update_source).await?; + let x = upsert_websocket_request(&app_handle, v, update_source).await?; imported_resources.websocket_requests.push(x.clone()); } info!("Imported {} websocket_requests", imported_resources.websocket_requests.len()); @@ -2686,12 +2701,12 @@ pub async fn batch_upsert( } pub async fn get_workspace_export_resources( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_ids: Vec<&str>, include_environments: bool, ) -> Result { let mut data = WorkspaceExport { - yaak_version: mgr.package_info().version.clone().to_string(), + yaak_version: app_handle.package_info().version.clone().to_string(), yaak_schema: 3, timestamp: Utc::now().naive_utc(), resources: BatchUpsertResult { @@ -2705,14 +2720,18 @@ pub async fn get_workspace_export_resources( }; for workspace_id in workspace_ids { - data.resources.workspaces.push(get_workspace(mgr, workspace_id).await?); - data.resources.environments.append(&mut list_environments(mgr, workspace_id).await?); - data.resources.folders.append(&mut list_folders(mgr, workspace_id).await?); - data.resources.http_requests.append(&mut list_http_requests(mgr, workspace_id).await?); - data.resources.grpc_requests.append(&mut list_grpc_requests(mgr, workspace_id).await?); + data.resources.workspaces.push(get_workspace(app_handle, workspace_id).await?); + data.resources.environments.append(&mut list_environments(app_handle, workspace_id).await?); + data.resources.folders.append(&mut list_folders(app_handle, workspace_id).await?); + data.resources + .http_requests + .append(&mut list_http_requests(app_handle, workspace_id).await?); + data.resources + .grpc_requests + .append(&mut list_grpc_requests(app_handle, workspace_id).await?); data.resources .websocket_requests - .append(&mut list_websocket_requests(mgr, workspace_id).await?); + .append(&mut list_websocket_requests(app_handle, workspace_id).await?); } // Nuke environments if we don't want them diff --git a/src-tauri/yaak-sync/src/commands.rs b/src-tauri/yaak-sync/src/commands.rs index 6f1b5474..b5cbe1ad 100644 --- a/src-tauri/yaak-sync/src/commands.rs +++ b/src-tauri/yaak-sync/src/commands.rs @@ -9,17 +9,17 @@ use log::warn; use serde::{Deserialize, Serialize}; use std::path::Path; use tauri::ipc::Channel; -use tauri::{command, Listener, Runtime, WebviewWindow}; +use tauri::{command, AppHandle, Listener, Runtime}; use tokio::sync::watch; use ts_rs::TS; #[command] pub async fn calculate( - window: WebviewWindow, + app_handle: AppHandle, workspace_id: &str, sync_dir: &Path, ) -> Result> { - let db_candidates = get_db_candidates(&window, workspace_id, sync_dir).await?; + let db_candidates = get_db_candidates(&app_handle, workspace_id, sync_dir).await?; let fs_candidates = get_fs_candidates(sync_dir) .await? .into_iter() @@ -40,13 +40,13 @@ pub async fn calculate_fs(dir: &Path) -> Result> { #[command] pub async fn apply( - window: WebviewWindow, + app_handle: AppHandle, sync_ops: Vec, sync_dir: &Path, workspace_id: &str, ) -> Result<()> { - let sync_state_ops = apply_sync_ops(&window, &workspace_id, sync_dir, sync_ops).await?; - apply_sync_state_ops(&window, workspace_id, sync_dir, sync_state_ops).await + let sync_state_ops = apply_sync_ops(&app_handle, &workspace_id, sync_dir, sync_ops).await?; + apply_sync_state_ops(&app_handle, workspace_id, sync_dir, sync_state_ops).await } #[derive(Debug, Clone, Serialize, Deserialize, TS)] @@ -58,7 +58,7 @@ pub(crate) struct WatchResult { #[command] pub async fn watch( - window: WebviewWindow, + app_handle: AppHandle, sync_dir: &Path, workspace_id: &str, channel: Channel, @@ -67,16 +67,16 @@ pub async fn watch( watch_directory(&sync_dir, channel, cancel_rx).await?; - let window_inner = window.clone(); + let app_handle_inner = app_handle.clone(); let unlisten_event = format!("watch-unlisten-{}-{}", workspace_id, Utc::now().timestamp_millis()); - // TODO: Figure out a way to unlisten when the client window refreshes or closes. Perhaps with + // TODO: Figure out a way to unlisten when the client app_handle refreshes or closes. Perhaps with // a heartbeat mechanism, or ensuring only a single subscription per workspace (at least // this won't create `n` subs). We could also maybe have a global fs watcher that we keep // adding to here. - window.listen_any(unlisten_event.clone(), move |event| { - window_inner.unlisten(event.id()); + app_handle.listen_any(unlisten_event.clone(), move |event| { + app_handle_inner.unlisten(event.id()); if let Err(e) = cancel_tx.send(()) { warn!("Failed to send cancel signal to watcher {e:?}"); } diff --git a/src-tauri/yaak-sync/src/sync.rs b/src-tauri/yaak-sync/src/sync.rs index 9d8728ff..b7482c8d 100644 --- a/src-tauri/yaak-sync/src/sync.rs +++ b/src-tauri/yaak-sync/src/sync.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::path::{Path, PathBuf}; -use tauri::{Manager, Runtime, WebviewWindow}; +use tauri::{AppHandle, Runtime}; use tokio::fs; use tokio::fs::File; use tokio::io::AsyncWriteExt; @@ -106,17 +106,21 @@ pub(crate) struct FsCandidate { } pub(crate) async fn get_db_candidates( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, sync_dir: &Path, ) -> Result> { - let models: HashMap<_, _> = - workspace_models(mgr, workspace_id).await?.into_iter().map(|m| (m.id(), m)).collect(); - let sync_states: HashMap<_, _> = list_sync_states_for_workspace(mgr, workspace_id, sync_dir) + let models: HashMap<_, _> = workspace_models(app_handle, workspace_id) .await? .into_iter() - .map(|s| (s.model_id.clone(), s)) + .map(|m| (m.id(), m)) .collect(); + let sync_states: HashMap<_, _> = + list_sync_states_for_workspace(app_handle, workspace_id, sync_dir) + .await? + .into_iter() + .map(|s| (s.model_id.clone(), s)) + .collect(); // 1. Add candidates for models (created/modified/unmodified) let mut candidates: Vec = models @@ -223,7 +227,7 @@ pub(crate) fn compute_sync_ops( model: model.to_owned(), state: sync_state.to_owned(), } - }, + } (Some(DbCandidate::Modified(model, sync_state)), None) => SyncOp::FsUpdate { model: model.to_owned(), state: sync_state.to_owned(), @@ -285,10 +289,11 @@ pub(crate) fn compute_sync_ops( } async fn workspace_models( - mgr: &impl Manager, + app_handle: &AppHandle, workspace_id: &str, ) -> Result> { - let resources = get_workspace_export_resources(mgr, vec![workspace_id], true).await?.resources; + let resources = + get_workspace_export_resources(app_handle, vec![workspace_id], true).await?.resources; let workspace = resources.workspaces.iter().find(|w| w.id == workspace_id); let workspace = match workspace { @@ -318,7 +323,7 @@ async fn workspace_models( } pub(crate) async fn apply_sync_ops( - window: &WebviewWindow, + app_handle: &AppHandle, workspace_id: &str, sync_dir: &Path, sync_ops: Vec, @@ -429,7 +434,7 @@ pub(crate) async fn apply_sync_ops( } } SyncOp::DbDelete { model, state } => { - delete_model(window, &model).await?; + delete_model(app_handle, &model).await?; SyncStateOp::Delete { state: state.to_owned(), } @@ -438,7 +443,7 @@ pub(crate) async fn apply_sync_ops( } let upserted_models = batch_upsert( - window, + app_handle, workspaces_to_upsert, environments_to_upsert, folders_to_upsert, @@ -452,7 +457,7 @@ pub(crate) async fn apply_sync_ops( // Ensure we creat WorkspaceMeta models for each new workspace, with the appropriate sync dir let sync_dir_string = sync_dir.to_string_lossy().to_string(); for workspace in upserted_models.workspaces { - let r = match get_workspace_meta(window, &workspace).await { + let r = match get_workspace_meta(app_handle, &workspace).await { Ok(Some(m)) => { if m.setting_sync_dir == Some(sync_dir_string.clone()) { // We don't need to update if unchanged @@ -462,7 +467,7 @@ pub(crate) async fn apply_sync_ops( setting_sync_dir: Some(sync_dir.to_string_lossy().to_string()), ..m }; - upsert_workspace_meta(window, wm, &UpdateSource::Sync).await + upsert_workspace_meta(app_handle, wm, &UpdateSource::Sync).await } Ok(None) => { let wm = WorkspaceMeta { @@ -470,7 +475,7 @@ pub(crate) async fn apply_sync_ops( setting_sync_dir: Some(sync_dir.to_string_lossy().to_string()), ..Default::default() }; - upsert_workspace_meta(window, wm, &UpdateSource::Sync).await + upsert_workspace_meta(app_handle, wm, &UpdateSource::Sync).await } Err(e) => Err(e), }; @@ -501,7 +506,7 @@ pub(crate) enum SyncStateOp { } pub(crate) async fn apply_sync_state_ops( - window: &WebviewWindow, + app_handle: &AppHandle, workspace_id: &str, sync_dir: &Path, ops: Vec, @@ -522,7 +527,7 @@ pub(crate) async fn apply_sync_state_ops( flushed_at: Utc::now().naive_utc(), ..Default::default() }; - upsert_sync_state(window, sync_state).await?; + upsert_sync_state(app_handle, sync_state).await?; } SyncStateOp::Update { state: sync_state, @@ -536,10 +541,10 @@ pub(crate) async fn apply_sync_state_ops( flushed_at: Utc::now().naive_utc(), ..sync_state }; - upsert_sync_state(window, sync_state).await?; + upsert_sync_state(app_handle, sync_state).await?; } SyncStateOp::Delete { state } => { - delete_sync_state(window, state.id.as_str()).await?; + delete_sync_state(app_handle, state.id.as_str()).await?; } } } @@ -551,25 +556,25 @@ fn derive_model_filename(m: &SyncModel) -> PathBuf { Path::new(&rel).to_path_buf() } -async fn delete_model(window: &WebviewWindow, model: &SyncModel) -> Result<()> { +async fn delete_model(app_handle: &AppHandle, model: &SyncModel) -> Result<()> { match model { SyncModel::Workspace(m) => { - delete_workspace(window, m.id.as_str(), &UpdateSource::Sync).await?; + delete_workspace(app_handle, m.id.as_str(), &UpdateSource::Sync).await?; } SyncModel::Environment(m) => { - delete_environment(window, m.id.as_str(), &UpdateSource::Sync).await?; + delete_environment(app_handle, m.id.as_str(), &UpdateSource::Sync).await?; } SyncModel::Folder(m) => { - delete_folder(window, m.id.as_str(), &UpdateSource::Sync).await?; + delete_folder(app_handle, m.id.as_str(), &UpdateSource::Sync).await?; } SyncModel::HttpRequest(m) => { - delete_http_request(window, m.id.as_str(), &UpdateSource::Sync).await?; + delete_http_request(app_handle, m.id.as_str(), &UpdateSource::Sync).await?; } SyncModel::GrpcRequest(m) => { - delete_grpc_request(window, m.id.as_str(), &UpdateSource::Sync).await?; + delete_grpc_request(app_handle, m.id.as_str(), &UpdateSource::Sync).await?; } SyncModel::WebsocketRequest(m) => { - delete_websocket_request(window, m.id.as_str(), &UpdateSource::Sync).await?; + delete_websocket_request(app_handle, m.id.as_str(), &UpdateSource::Sync).await?; } }; Ok(()) diff --git a/src-tauri/yaak-templates/src/renderer.rs b/src-tauri/yaak-templates/src/renderer.rs index 989c61a6..a31c10e6 100644 --- a/src-tauri/yaak-templates/src/renderer.rs +++ b/src-tauri/yaak-templates/src/renderer.rs @@ -1,7 +1,6 @@ use crate::error::Error::{RenderStackExceededError, VariableNotFound}; use crate::error::Result; use crate::{Parser, Token, Tokens, Val}; -use log::warn; use serde_json::json; use std::collections::HashMap; use std::future::Future; @@ -113,13 +112,7 @@ async fn render_value( let v = Box::pin(render_value(a.value, vars, cb, depth)).await?; resolved_args.insert(a.name, v); } - match cb.run(name.as_str(), resolved_args.clone()).await { - Ok(s) => s, - Err(e) => { - warn!("Failed to run template callback {}({:?}): {}", name, resolved_args, e); - "".to_string() - } - } + cb.run(name.as_str(), resolved_args.clone()).await? } Val::Null => "".into(), }; @@ -324,7 +317,7 @@ mod parse_and_render_tests { #[tokio::test] async fn render_fn_err() -> Result<()> { let vars = HashMap::new(); - let template = r#"${[ error() ]}"#; + let template = r#"hello ${[ error() ]}"#; struct CB {} impl TemplateCallback for CB { diff --git a/src-tauri/yaak-ws/src/commands.rs b/src-tauri/yaak-ws/src/commands.rs index c62a3c3b..b8e533fe 100644 --- a/src-tauri/yaak-ws/src/commands.rs +++ b/src-tauri/yaak-ws/src/commands.rs @@ -5,7 +5,7 @@ use crate::render::render_request; use log::{info, warn}; use std::str::FromStr; use tauri::http::{HeaderMap, HeaderName}; -use tauri::{AppHandle, Manager, Runtime, State, Url, WebviewWindow}; +use tauri::{AppHandle, Runtime, State, Url, WebviewWindow}; use tokio::sync::{mpsc, Mutex}; use tokio_tungstenite::tungstenite::http::HeaderValue; use tokio_tungstenite::tungstenite::Message; @@ -28,41 +28,54 @@ use yaak_plugins::template_callback::PluginTemplateCallback; #[tauri::command] pub(crate) async fn upsert_request( request: WebsocketRequest, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - Ok(queries::upsert_websocket_request(&w, request, &UpdateSource::Window).await?) + Ok(queries::upsert_websocket_request(&app_handle, request, &UpdateSource::from_window(&window)) + .await?) } #[tauri::command] pub(crate) async fn duplicate_request( request_id: &str, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - Ok(queries::duplicate_websocket_request(&w, request_id, &UpdateSource::Window).await?) + Ok(queries::duplicate_websocket_request( + &app_handle, + request_id, + &UpdateSource::from_window(&window), + ) + .await?) } #[tauri::command] pub(crate) async fn delete_request( request_id: &str, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - Ok(queries::delete_websocket_request(&w, request_id, &UpdateSource::Window).await?) + Ok(queries::delete_websocket_request(&app_handle, request_id, &UpdateSource::from_window(&window)).await?) } #[tauri::command] pub(crate) async fn delete_connection( connection_id: &str, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result { - Ok(queries::delete_websocket_connection(&w, connection_id, &UpdateSource::Window).await?) + Ok(queries::delete_websocket_connection(&app_handle, connection_id, &UpdateSource::from_window(&window)) + .await?) } #[tauri::command] pub(crate) async fn delete_connections( request_id: &str, - w: WebviewWindow, + app_handle: AppHandle, + window: WebviewWindow, ) -> Result<()> { - Ok(queries::delete_all_websocket_connections(&w, request_id, &UpdateSource::Window).await?) + Ok(queries::delete_all_websocket_connections(&app_handle, request_id, &UpdateSource::from_window(&window)) + .await?) } #[tauri::command] @@ -93,24 +106,26 @@ pub(crate) async fn list_connections( pub(crate) async fn send( connection_id: &str, environment_id: Option<&str>, + app_handle: AppHandle, window: WebviewWindow, ws_manager: State<'_, Mutex>, ) -> Result { - let connection = get_websocket_connection(&window, connection_id).await?; - let unrendered_request = get_websocket_request(&window, &connection.request_id) + let connection = get_websocket_connection(&app_handle, connection_id).await?; + let unrendered_request = get_websocket_request(&app_handle, &connection.request_id) .await? .ok_or(GenericError("WebSocket Request not found".to_string()))?; let environment = match environment_id { - Some(id) => Some(get_environment(&window, id).await?), + Some(id) => Some(get_environment(&app_handle, id).await?), None => None, }; - let base_environment = get_base_environment(&window, &unrendered_request.workspace_id).await?; + let base_environment = + get_base_environment(&app_handle, &unrendered_request.workspace_id).await?; let request = render_request( &unrendered_request, &base_environment, environment.as_ref(), &PluginTemplateCallback::new( - window.app_handle(), + &app_handle, &WindowContext::from_window(&window), RenderPurpose::Send, ), @@ -121,7 +136,7 @@ pub(crate) async fn send( ws_manager.send(&connection.id, Message::Text(request.message.clone().into())).await?; upsert_websocket_event( - &window, + &app_handle, WebsocketEvent { connection_id: connection.id.clone(), request_id: request.id.clone(), @@ -131,7 +146,7 @@ pub(crate) async fn send( message: request.message.into(), ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -142,17 +157,18 @@ pub(crate) async fn send( #[tauri::command] pub(crate) async fn close( connection_id: &str, + app_handle: AppHandle, window: WebviewWindow, ws_manager: State<'_, Mutex>, ) -> Result { - let connection = get_websocket_connection(&window, connection_id).await?; + let connection = get_websocket_connection(&app_handle, connection_id).await?; let connection = upsert_websocket_connection( - &window, + &app_handle, &WebsocketConnection { state: WebsocketConnectionState::Closing, ..connection }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -170,24 +186,26 @@ pub(crate) async fn connect( request_id: &str, environment_id: Option<&str>, cookie_jar_id: Option<&str>, + app_handle: AppHandle, window: WebviewWindow, plugin_manager: State<'_, PluginManager>, ws_manager: State<'_, Mutex>, ) -> Result { - let unrendered_request = get_websocket_request(&window, request_id) + let unrendered_request = get_websocket_request(&app_handle, request_id) .await? .ok_or(GenericError("Failed to find GRPC request".to_string()))?; let environment = match environment_id { - Some(id) => Some(get_environment(&window, id).await?), + Some(id) => Some(get_environment(&app_handle, id).await?), None => None, }; - let base_environment = get_base_environment(&window, &unrendered_request.workspace_id).await?; + let base_environment = + get_base_environment(&app_handle, &unrendered_request.workspace_id).await?; let request = render_request( &unrendered_request, &base_environment, environment.as_ref(), &PluginTemplateCallback::new( - window.app_handle(), + &app_handle, &WindowContext::from_window(&window), RenderPurpose::Send, ), @@ -224,18 +242,18 @@ pub(crate) async fn connect( // TODO: Handle cookies let _cookie_jar = match cookie_jar_id { - Some(id) => Some(get_cookie_jar(&window, id).await?), + Some(id) => Some(get_cookie_jar(&app_handle, id).await?), None => None, }; let connection = upsert_websocket_connection( - &window, + &app_handle, &WebsocketConnection { workspace_id: request.workspace_id.clone(), request_id: request_id.to_string(), ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await?; @@ -261,20 +279,20 @@ pub(crate) async fn connect( Ok(r) => r, Err(e) => { return Ok(upsert_websocket_connection( - &window, + &app_handle, &WebsocketConnection { error: Some(format!("{e:?}")), state: WebsocketConnectionState::Closed, ..connection }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await?); } }; upsert_websocket_event( - &window, + &app_handle, WebsocketEvent { connection_id: connection.id.clone(), request_id: request.id.clone(), @@ -283,7 +301,7 @@ pub(crate) async fn connect( message_type: WebsocketEventType::Open, ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -298,7 +316,7 @@ pub(crate) async fn connect( .collect::>(); let connection = upsert_websocket_connection( - &window, + &app_handle, &WebsocketConnection { state: WebsocketConnectionState::Connected, headers: response_headers, @@ -306,7 +324,7 @@ pub(crate) async fn connect( url: request.url.clone(), ..connection }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await?; @@ -314,7 +332,6 @@ pub(crate) async fn connect( let connection_id = connection.id.clone(); let request_id = request.id.to_string(); let workspace_id = request.workspace_id.clone(); - let window = window.clone(); let connection = connection.clone(); let mut has_written_close = false; tokio::spawn(async move { @@ -324,7 +341,7 @@ pub(crate) async fn connect( } upsert_websocket_event( - &window, + &app_handle, WebsocketEvent { connection_id: connection_id.clone(), request_id: request_id.clone(), @@ -342,7 +359,7 @@ pub(crate) async fn connect( message: message.into_data().into(), ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); @@ -350,7 +367,7 @@ pub(crate) async fn connect( info!("Websocket connection closed"); if !has_written_close { upsert_websocket_event( - &window, + &app_handle, WebsocketEvent { connection_id: connection_id.clone(), request_id: request_id.clone(), @@ -359,20 +376,20 @@ pub(crate) async fn connect( message_type: WebsocketEventType::Close, ..Default::default() }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); } upsert_websocket_connection( - &window, + &app_handle, &WebsocketConnection { workspace_id: request.workspace_id.clone(), request_id: request_id.to_string(), state: WebsocketConnectionState::Closed, ..connection }, - &UpdateSource::Window, + &UpdateSource::from_window(&window), ) .await .unwrap(); diff --git a/src-web/components/GrpcConnectionSetupPane.tsx b/src-web/components/GrpcConnectionSetupPane.tsx index c5436e25..efc9015d 100644 --- a/src-web/components/GrpcConnectionSetupPane.tsx +++ b/src-web/components/GrpcConnectionSetupPane.tsx @@ -1,12 +1,11 @@ import type { GrpcMetadataEntry, GrpcRequest } from '@yaakapp-internal/models'; import classNames from 'classnames'; -import { useAtom } from 'jotai'; -import { atomWithStorage } from 'jotai/utils'; import type { CSSProperties } from 'react'; import React, { useCallback, useMemo, useRef } from 'react'; import { useContainerSize } from '../hooks/useContainerQuery'; import type { ReflectResponseService } from '../hooks/useGrpc'; import { useHttpAuthenticationSummaries } from '../hooks/useHttpAuthentication'; +import { useKeyValue } from '../hooks/useKeyValue'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest'; import { resolvedModelName } from '../lib/resolvedModelName'; @@ -52,8 +51,6 @@ const TAB_METADATA = 'metadata'; const TAB_AUTH = 'auth'; const TAB_DESCRIPTION = 'description'; -const tabsAtom = atomWithStorage>('grpcRequestPaneActiveTabs', {}); - export function GrpcConnectionSetupPane({ style, services, @@ -70,7 +67,11 @@ export function GrpcConnectionSetupPane({ }: Props) { const updateRequest = useUpdateAnyGrpcRequest(); const authentication = useHttpAuthenticationSummaries(); - const [activeTabs, setActiveTabs] = useAtom(tabsAtom); + const { value: activeTabs, set: setActiveTabs } = useKeyValue>({ + namespace: 'no_sync', + key: 'grpcRequestActiveTabs', + fallback: {}, + }); const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null); const urlContainerEl = useRef(null); @@ -183,8 +184,8 @@ export function GrpcConnectionSetupPane({ const activeTab = activeTabs?.[activeRequest.id]; const setActiveTab = useCallback( - (tab: string) => { - setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab })); + async (tab: string) => { + await setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab })); }, [activeRequest.id, setActiveTabs], ); diff --git a/src-web/components/HttpRequestPane.tsx b/src-web/components/HttpRequestPane.tsx index a7872be1..38c88ba6 100644 --- a/src-web/components/HttpRequestPane.tsx +++ b/src-web/components/HttpRequestPane.tsx @@ -1,8 +1,7 @@ import type { HttpRequest } from '@yaakapp-internal/models'; import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; import classNames from 'classnames'; -import { atom, useAtom, useAtomValue } from 'jotai'; -import { atomWithStorage } from 'jotai/utils'; +import { atom, useAtomValue } from 'jotai'; import type { CSSProperties } from 'react'; import React, { useCallback, useMemo, useState } from 'react'; import { activeRequestIdAtom } from '../hooks/useActiveRequestId'; @@ -11,6 +10,7 @@ import { grpcRequestsAtom } from '../hooks/useGrpcRequests'; import { useHttpAuthenticationSummaries } from '../hooks/useHttpAuthentication'; import { httpRequestsAtom } from '../hooks/useHttpRequests'; import { useImportCurl } from '../hooks/useImportCurl'; +import { useKeyValue } from '../hooks/useKeyValue'; import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse'; import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; @@ -27,7 +27,8 @@ import { BODY_TYPE_JSON, BODY_TYPE_NONE, BODY_TYPE_OTHER, - BODY_TYPE_XML, getContentTypeFromHeaders, + BODY_TYPE_XML, + getContentTypeFromHeaders, } from '../lib/model_util'; import { prepareImportQuerystring } from '../lib/prepareImportQuerystring'; import { resolvedModelName } from '../lib/resolvedModelName'; @@ -64,8 +65,6 @@ const TAB_HEADERS = 'headers'; const TAB_AUTH = 'auth'; const TAB_DESCRIPTION = 'description'; -const tabsAtom = atomWithStorage>('requestPaneActiveTabs', {}); - const nonActiveRequestUrlsAtom = atom((get) => { const activeRequestId = get(activeRequestIdAtom); const requests = [...get(httpRequestsAtom), ...get(grpcRequestsAtom)]; @@ -79,7 +78,11 @@ const memoNotActiveRequestUrlsAtom = deepEqualAtom(nonActiveRequestUrlsAtom); export function HttpRequestPane({ style, fullHeight, className, activeRequest }: Props) { const activeRequestId = activeRequest.id; const { mutateAsync: updateRequestAsync, mutate: updateRequest } = useUpdateAnyHttpRequest(); - const [activeTabs, setActiveTabs] = useAtom(tabsAtom); + const { value: activeTabs, set: setActiveTabs } = useKeyValue>({ + namespace: 'no_sync', + key: 'httpRequestActiveTabs', + fallback: {}, + }); const [forceUpdateHeaderEditorKey, setForceUpdateHeaderEditorKey] = useState(0); const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null); const [{ urlKey }, { focusParamsTab, forceUrlRefresh, forceParamsRefresh }] = useRequestEditor(); @@ -285,14 +288,14 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }: const activeTab = activeTabs?.[activeRequestId]; const setActiveTab = useCallback( - (tab: string) => { - setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab })); + async (tab: string) => { + await setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab })); }, [activeRequest.id, setActiveTabs], ); - useRequestEditorEvent('request_pane.focus_tab', () => { - setActiveTab(TAB_PARAMS); + useRequestEditorEvent('request_pane.focus_tab', async () => { + await setActiveTab(TAB_PARAMS); }); const autocompleteUrls = useAtomValue(memoNotActiveRequestUrlsAtom); diff --git a/src-web/components/WebsocketRequestPane.tsx b/src-web/components/WebsocketRequestPane.tsx index bd26e5a7..a56e7632 100644 --- a/src-web/components/WebsocketRequestPane.tsx +++ b/src-web/components/WebsocketRequestPane.tsx @@ -2,8 +2,7 @@ import type { HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; import { closeWebsocket, connectWebsocket, sendWebsocket } from '@yaakapp-internal/ws'; import classNames from 'classnames'; -import { atom, useAtom, useAtomValue } from 'jotai'; -import { atomWithStorage } from 'jotai/utils'; +import { atom, useAtomValue } from 'jotai'; import type { CSSProperties } from 'react'; import React, { useCallback, useMemo } from 'react'; import { upsertWebsocketRequest } from '../commands/upsertWebsocketRequest'; @@ -12,6 +11,7 @@ import { getActiveEnvironment } from '../hooks/useActiveEnvironment'; import { activeRequestIdAtom } from '../hooks/useActiveRequestId'; import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse'; import { useHttpAuthenticationSummaries } from '../hooks/useHttpAuthentication'; +import { useKeyValue } from '../hooks/useKeyValue'; import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse'; import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor'; import { requestsAtom } from '../hooks/useRequests'; @@ -50,8 +50,6 @@ const TAB_HEADERS = 'headers'; const TAB_AUTH = 'auth'; const TAB_DESCRIPTION = 'description'; -const tabsAtom = atomWithStorage>('requestPaneActiveTabs', {}); - const nonActiveRequestUrlsAtom = atom((get) => { const activeRequestId = get(activeRequestIdAtom); const requests = get(requestsAtom); @@ -64,7 +62,11 @@ const memoNotActiveRequestUrlsAtom = deepEqualAtom(nonActiveRequestUrlsAtom); export function WebsocketRequestPane({ style, fullHeight, className, activeRequest }: Props) { const activeRequestId = activeRequest.id; - const [activeTabs, setActiveTabs] = useAtom(tabsAtom); + const { value: activeTabs, set: setActiveTabs } = useKeyValue>({ + namespace: 'no_sync', + key: 'websocketRequestActiveTabs', + fallback: {}, + }); const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null); const [{ urlKey }, { focusParamsTab, forceUrlRefresh, forceParamsRefresh }] = useRequestEditor(); const authentication = useHttpAuthenticationSummaries(); @@ -157,14 +159,14 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque const activeTab = activeTabs?.[activeRequestId]; const setActiveTab = useCallback( - (tab: string) => { - setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab })); + async (tab: string) => { + await setActiveTabs((r) => ({ ...r, [activeRequest.id]: tab })); }, [activeRequest.id, setActiveTabs], ); - useRequestEditorEvent('request_pane.focus_tab', () => { - setActiveTab(TAB_PARAMS); + useRequestEditorEvent('request_pane.focus_tab', async () => { + await setActiveTab(TAB_PARAMS); }); const autocompleteUrls = useAtomValue(memoNotActiveRequestUrlsAtom); diff --git a/src-web/hooks/useSyncModelStores.ts b/src-web/hooks/useSyncModelStores.ts index ed677a1b..bc9d51f2 100644 --- a/src-web/hooks/useSyncModelStores.ts +++ b/src-web/hooks/useSyncModelStores.ts @@ -5,7 +5,7 @@ import type { AnyModel, KeyValue, ModelPayload } from '@yaakapp-internal/models' import { jotaiStore } from '../lib/jotai'; import { buildKeyValueKey } from '../lib/keyValueStore'; import { modelsEq } from '../lib/model_util'; -import { useActiveWorkspace } from './useActiveWorkspace'; +import { getActiveWorkspaceId } from './useActiveWorkspace'; import { cookieJarsAtom } from './useCookieJars'; import { environmentsAtom } from './useEnvironments'; import { foldersAtom } from './useFolders'; @@ -26,7 +26,6 @@ import { workspaceMetaAtom } from './useWorkspaceMeta'; import { workspacesAtom } from './useWorkspaces'; export function useSyncModelStores() { - const activeWorkspace = useActiveWorkspace(); const queryClient = useQueryClient(); const { wasUpdatedExternally } = useRequestUpdateKey(null); @@ -45,16 +44,13 @@ export function useSyncModelStores() { (payload.model.model === 'http_request' || payload.model.model === 'grpc_request' || payload.model.model === 'websocket_request') && - (payload.windowLabel !== getCurrentWebviewWindow().label || payload.updateSource !== 'window') + ((payload.updateSource.type === 'window' && + payload.updateSource.label !== getCurrentWebviewWindow().label) || + payload.updateSource.type !== 'window') ) { wasUpdatedExternally(payload.model.id); } - // Only sync models that belong to this workspace, if a workspace ID is present - if ('workspaceId' in payload.model && payload.model.workspaceId !== activeWorkspace?.id) { - return; - } - if (shouldIgnoreModel(payload)) return; if (payload.model.model === 'workspace') { @@ -160,7 +156,7 @@ export function removeModelById(model: T) { return (prevEntries: T[] | undefined) => { const entries = prevEntries?.filter((e) => e.id !== model.id) ?? []; - // Don't trigger an update if we didn't actually remove anything + // Don't trigger an update if we didn't remove anything if (entries.length === (prevEntries ?? []).length) { return prevEntries ?? []; } @@ -181,17 +177,24 @@ export function removeModelByKv(model: KeyValue) { ) ?? []; } -function shouldIgnoreModel({ model, windowLabel, updateSource }: ModelPayload) { - // Never ignore same-window updates - if (windowLabel === getCurrentWebviewWindow().label) { +function shouldIgnoreModel({ model, updateSource }: ModelPayload) { + console.log('HELLO', updateSource); + // Never ignore updates from non-user sources + if (updateSource.type !== 'window') { return false; } - // Never ignore updates from non-user sources - if (updateSource !== 'window') { + // Never ignore same-window updates + if (updateSource.label === getCurrentWebviewWindow().label) { return false; } + const activeWorkspaceId = getActiveWorkspaceId(); + // Only sync models that belong to this workspace, if a workspace ID is present + if ('workspaceId' in model && model.workspaceId !== activeWorkspaceId) { + return; + } + if (model.model === 'key_value') { return model.namespace === 'no_sync'; }