Fix DB mutex deadlock

This commit is contained in:
Gregory Schier
2024-02-04 21:17:05 -08:00
parent acb01cf086
commit 7bb620e6d5
9 changed files with 312 additions and 348 deletions
@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO grpc_connections (\n id, workspace_id, request_id, service, method\n )\n VALUES ( ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n service = excluded.service,\n method = excluded.method\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 5
},
"nullable": []
},
"hash": "1ad8a2581417acb2d1b06fc2727a452230ff4ecbdfb5be54a034ec7c4fb1cabe"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO grpc_connections (\n id, workspace_id, request_id, service, method\n )\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n service = excluded.service,\n method = excluded.method\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 5
},
"nullable": []
},
"hash": "f7df06213eff80e2ce5100b77ec244c83de39048e77c5af0b0b5d188d3279ca4"
}
-2
View File
@@ -1,8 +1,6 @@
use prost_reflect::SerializeOptions; use prost_reflect::SerializeOptions;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio_stream::Stream;
use tonic::transport::Uri; use tonic::transport::Uri;
use tonic::IntoRequest;
use crate::proto::fill_pool; use crate::proto::fill_pool;
-1
View File
@@ -1,6 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use hyper::client::connect::Connect;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::Client; use hyper::Client;
use hyper_rustls::HttpsConnector; use hyper_rustls::HttpsConnector;
+2 -2
View File
@@ -366,7 +366,7 @@ pub async fn send_http_request(
.await .await
.expect("Failed to update response"); .expect("Failed to update response");
if !request.id.is_empty() { if !request.id.is_empty() {
emit_side_effect(app_handle.clone(), "updated_model", &response); emit_side_effect(app_handle.clone(), "upserted_model", &response);
} }
// Copy response to download path, if specified // Copy response to download path, if specified
@@ -399,7 +399,7 @@ pub async fn send_http_request(
cookie_jar.cookies = json_cookies; cookie_jar.cookies = json_cookies;
match models::upsert_cookie_jar(db, &cookie_jar).await { match models::upsert_cookie_jar(db, &cookie_jar).await {
Ok(updated_jar) => { Ok(updated_jar) => {
emit_side_effect(app_handle, "updated_model", &updated_jar); emit_side_effect(app_handle, "upserted_model", &updated_jar);
} }
Err(e) => { Err(e) => {
error!("Failed to update cookie jar: {}", e); error!("Failed to update cookie jar: {}", e);
+63 -152
View File
@@ -102,16 +102,14 @@ async fn cmd_grpc_call_unary(
request_id: &str, request_id: &str,
app_handle: AppHandle<Wry>, app_handle: AppHandle<Wry>,
grpc_handle: State<'_, Mutex<GrpcManager>>, grpc_handle: State<'_, Mutex<GrpcManager>>,
db_state: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<GrpcMessage, String> { ) -> Result<GrpcMessage, String> {
let db = &*db_state.lock().await; let req = get_grpc_request(&app_handle, request_id)
let req = get_grpc_request(db, request_id)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let conn = { let conn = {
let req = req.clone(); let req = req.clone();
upsert_grpc_connection( upsert_grpc_connection(
db, &app_handle,
&GrpcConnection { &GrpcConnection {
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
request_id: req.id, request_id: req.id,
@@ -127,7 +125,7 @@ async fn cmd_grpc_call_unary(
let req = req.clone(); let req = req.clone();
let conn = conn.clone(); let conn = conn.clone();
upsert_grpc_message( upsert_grpc_message(
db, &app_handle,
&GrpcMessage { &GrpcMessage {
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
request_id: req.id, request_id: req.id,
@@ -157,7 +155,7 @@ async fn cmd_grpc_call_unary(
{ {
Ok(msg) => { Ok(msg) => {
upsert_grpc_message( upsert_grpc_message(
db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: msg, message: msg,
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -178,18 +176,15 @@ async fn cmd_grpc_call_unary(
#[tauri::command] #[tauri::command]
async fn cmd_grpc_client_streaming( async fn cmd_grpc_client_streaming(
request_id: &str, request_id: &str,
grpc_handle: State<'_, Mutex<GrpcManager>>,
app_handle: AppHandle<Wry>, app_handle: AppHandle<Wry>,
db_state: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<GrpcConnection, String> { ) -> Result<GrpcConnection, String> {
let db = &*db_state.lock().await; let req = get_grpc_request(&app_handle, request_id)
let req = get_grpc_request(db, request_id)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let conn = { let conn = {
let req = req.clone(); let req = req.clone();
upsert_grpc_connection( upsert_grpc_connection(
db, &app_handle,
&GrpcConnection { &GrpcConnection {
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
request_id: req.id, request_id: req.id,
@@ -204,12 +199,8 @@ async fn cmd_grpc_client_streaming(
{ {
let conn = conn.clone(); let conn = conn.clone();
let req = req.clone(); let req = req.clone();
let db = db.clone();
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Initiating connection".to_string(), message: "Initiating connection".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -220,8 +211,7 @@ async fn cmd_grpc_client_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .unwrap();
);
}; };
let (in_msg_tx, in_msg_rx) = tauri::async_runtime::channel::<String>(16); let (in_msg_tx, in_msg_rx) = tauri::async_runtime::channel::<String>(16);
@@ -277,13 +267,8 @@ async fn cmd_grpc_client_streaming(
let req = req.clone(); let req = req.clone();
let conn = conn.clone(); let conn = conn.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: msg, message: msg,
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -293,8 +278,7 @@ async fn cmd_grpc_client_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .unwrap();
);
}); });
} }
Ok(IncomingMsg::Commit) => { Ok(IncomingMsg::Commit) => {
@@ -318,19 +302,14 @@ async fn cmd_grpc_client_streaming(
let req = req.clone(); let req = req.clone();
async move { async move {
let grpc_handle = app_handle.state::<Mutex<GrpcManager>>(); let grpc_handle = app_handle.state::<Mutex<GrpcManager>>();
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let msg = grpc_handle let msg = grpc_handle
.lock() .lock()
.await .await
.client_streaming(&conn.id, uri, &service, &method, in_msg_stream) .client_streaming(&conn.id, uri, &service, &method, in_msg_stream)
.await .await
.unwrap(); .unwrap();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: msg.to_string(), message: msg.to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -341,23 +320,18 @@ async fn cmd_grpc_client_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .unwrap();
);
} }
}; };
{ {
let conn = conn.clone(); let conn = conn.clone();
let app_handle = app_handle.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
tokio::select! { tokio::select! {
_ = grpc_listen => { _ = grpc_listen => {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Connection completed".to_string(), message: "Connection completed".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -367,18 +341,11 @@ async fn cmd_grpc_client_streaming(
..Default::default() ..Default::default()
}, },
) )
.await .await.map_err(|e| e.to_string()).unwrap();
.expect("Failed to upsert message"),
);
}, },
_ = cancelled_rx.changed() => { _ = cancelled_rx.changed() => {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Connection cancelled".to_string(), message: "Connection cancelled".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -389,8 +356,7 @@ async fn cmd_grpc_client_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .map_err(|e| e.to_string()).unwrap();
);
}, },
} }
app_handle.unlisten(event_handler); app_handle.unlisten(event_handler);
@@ -405,16 +371,14 @@ async fn cmd_grpc_streaming(
request_id: &str, request_id: &str,
app_handle: AppHandle<Wry>, app_handle: AppHandle<Wry>,
grpc_handle: State<'_, Mutex<GrpcManager>>, grpc_handle: State<'_, Mutex<GrpcManager>>,
db_state: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<String, String> { ) -> Result<String, String> {
let db = &*db_state.lock().await; let req = get_grpc_request(&app_handle, request_id)
let req = get_grpc_request(db, request_id)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let conn = { let conn = {
let req = req.clone(); let req = req.clone();
upsert_grpc_connection( upsert_grpc_connection(
db, &app_handle,
&GrpcConnection { &GrpcConnection {
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
request_id: req.id, request_id: req.id,
@@ -429,12 +393,8 @@ async fn cmd_grpc_streaming(
{ {
let conn = conn.clone(); let conn = conn.clone();
let req = req.clone(); let req = req.clone();
let db = db.clone();
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Initiating connection".to_string(), message: "Initiating connection".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -445,8 +405,7 @@ async fn cmd_grpc_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .expect("Failed to upsert message");
);
}; };
let (in_msg_tx, in_msg_rx) = tauri::async_runtime::channel::<String>(16); let (in_msg_tx, in_msg_rx) = tauri::async_runtime::channel::<String>(16);
@@ -496,13 +455,8 @@ async fn cmd_grpc_streaming(
let req = req.clone(); let req = req.clone();
let conn = conn.clone(); let conn = conn.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: msg, message: msg,
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -512,8 +466,8 @@ async fn cmd_grpc_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .map_err(|e| e.to_string())
); .unwrap();
}); });
} }
Ok(IncomingMsg::Cancel) => { Ok(IncomingMsg::Cancel) => {
@@ -539,13 +493,8 @@ async fn cmd_grpc_streaming(
let item = serde_json::to_string_pretty(&item).unwrap(); let item = serde_json::to_string_pretty(&item).unwrap();
let req = req.clone(); let req = req.clone();
let conn = conn.clone(); let conn = conn.clone();
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: item, message: item,
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -556,8 +505,8 @@ async fn cmd_grpc_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .map_err(|e| e.to_string())
); .unwrap();
} }
Some(Err(e)) => { Some(Err(e)) => {
error!("gRPC stream error: {:?}", e); error!("gRPC stream error: {:?}", e);
@@ -575,15 +524,11 @@ async fn cmd_grpc_streaming(
{ {
let conn = conn.clone(); let conn = conn.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
let app_handle = app_handle.clone();
tokio::select! { tokio::select! {
_ = grpc_listen => { _ = grpc_listen => {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Connection completed".to_string(), message: "Connection completed".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -593,18 +538,11 @@ async fn cmd_grpc_streaming(
..Default::default() ..Default::default()
}, },
) )
.await .await.map_err(|e| e.to_string()).unwrap();
.expect("Failed to upsert message"),
);
}, },
_ = cancelled_rx.changed() => { _ = cancelled_rx.changed() => {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Connection cancelled".to_string(), message: "Connection cancelled".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -614,9 +552,7 @@ async fn cmd_grpc_streaming(
..Default::default() ..Default::default()
}, },
) )
.await .await.map_err(|e| e.to_string()).unwrap();
.expect("Failed to upsert message"),
);
}, },
} }
app_handle.unlisten(event_handler); app_handle.unlisten(event_handler);
@@ -631,17 +567,15 @@ async fn cmd_grpc_server_streaming(
request_id: &str, request_id: &str,
app_handle: AppHandle<Wry>, app_handle: AppHandle<Wry>,
grpc_handle: State<'_, Mutex<GrpcManager>>, grpc_handle: State<'_, Mutex<GrpcManager>>,
db_state: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<GrpcConnection, String> { ) -> Result<GrpcConnection, String> {
let db = &*db_state.lock().await; let req = get_grpc_request(&app_handle, request_id)
let req = get_grpc_request(db, request_id)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let conn = { let conn = {
let req = req.clone(); let req = req.clone();
upsert_grpc_connection( upsert_grpc_connection(
db, &app_handle,
&GrpcConnection { &GrpcConnection {
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
request_id: req.id, request_id: req.id,
@@ -656,11 +590,8 @@ async fn cmd_grpc_server_streaming(
{ {
let req = req.clone(); let req = req.clone();
let conn = conn.clone(); let conn = conn.clone();
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Initiating connection".to_string(), message: "Initiating connection".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -671,8 +602,7 @@ async fn cmd_grpc_server_streaming(
}, },
) )
.await .await
.expect("Failed to upsert message"), .unwrap();
);
} }
let (cancelled_tx, mut cancelled_rx) = tokio::sync::watch::channel(false); let (cancelled_tx, mut cancelled_rx) = tokio::sync::watch::channel(false);
@@ -718,7 +648,6 @@ async fn cmd_grpc_server_streaming(
app_handle.listen_global(format!("grpc_client_msg_{}", conn.id).as_str(), cb); app_handle.listen_global(format!("grpc_client_msg_{}", conn.id).as_str(), cb);
let grpc_listen = { let grpc_listen = {
let db = db.clone();
let conn_id = conn.clone().id; let conn_id = conn.clone().id;
let app_handle = app_handle.clone(); let app_handle = app_handle.clone();
let req = req.clone(); let req = req.clone();
@@ -726,11 +655,12 @@ async fn cmd_grpc_server_streaming(
loop { loop {
let req = req.clone(); let req = req.clone();
let conn_id = conn_id.clone(); let conn_id = conn_id.clone();
let app_handle = app_handle.clone();
match stream.next().await { match stream.next().await {
Some(Ok(item)) => { Some(Ok(item)) => {
let item = serde_json::to_string_pretty(&item).unwrap(); let item = serde_json::to_string_pretty(&item).unwrap();
let msg = upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: item, message: item,
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -743,7 +673,6 @@ async fn cmd_grpc_server_streaming(
.await .await
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
.expect("Failed to upsert message"); .expect("Failed to upsert message");
emit_side_effect(app_handle.clone(), "created_model", msg);
} }
Some(Err(e)) => { Some(Err(e)) => {
error!("gRPC stream error: {:?}", e); error!("gRPC stream error: {:?}", e);
@@ -761,16 +690,12 @@ async fn cmd_grpc_server_streaming(
{ {
let conn = conn.clone(); let conn = conn.clone();
let req = req.clone(); let req = req.clone();
let app_handle = app_handle.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
tokio::select! { tokio::select! {
_ = grpc_listen => { _ = grpc_listen => {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Connection completed".to_string(), message: "Connection completed".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -780,18 +705,11 @@ async fn cmd_grpc_server_streaming(
..Default::default() ..Default::default()
}, },
) )
.await .await.unwrap();
.expect("Failed to upsert message"),
);
}, },
_ = cancelled_rx.changed() => { _ = cancelled_rx.changed() => {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
emit_side_effect(
app_handle.clone(),
"created_model",
upsert_grpc_message( upsert_grpc_message(
&db, &app_handle,
&GrpcMessage { &GrpcMessage {
message: "Connection cancelled".to_string(), message: "Connection cancelled".to_string(),
workspace_id: req.workspace_id, workspace_id: req.workspace_id,
@@ -801,9 +719,7 @@ async fn cmd_grpc_server_streaming(
..Default::default() ..Default::default()
}, },
) )
.await .await.unwrap();
.expect("Failed to upsert message"),
);
}, },
} }
app_handle.unlisten(event_handler); app_handle.unlisten(event_handler);
@@ -1076,7 +992,7 @@ async fn response_err(
response = update_response_if_id(db, &response) response = update_response_if_id(db, &response)
.await .await
.expect("Failed to update response"); .expect("Failed to update response");
emit_side_effect(app_handle, "updated_model", &response); emit_side_effect(app_handle, "upserted_model", &response);
Ok(response) Ok(response)
} }
@@ -1136,7 +1052,7 @@ async fn cmd_set_key_value(
if created { if created {
emit_and_return(&window, "created_model", key_value) emit_and_return(&window, "created_model", key_value)
} else { } else {
emit_and_return(&window, "updated_model", key_value) emit_and_return(&window, "upserted_model", key_value)
} }
} }
@@ -1166,7 +1082,7 @@ async fn cmd_update_cookie_jar(
.await .await
.expect("Failed to update cookie jar"); .expect("Failed to update cookie jar");
emit_and_return(&window, "updated_model", updated) emit_and_return(&window, "upserted_model", updated)
} }
#[tauri::command] #[tauri::command]
@@ -1235,11 +1151,10 @@ async fn cmd_create_grpc_request(
sort_priority: f64, sort_priority: f64,
folder_id: Option<&str>, folder_id: Option<&str>,
window: Window<Wry>, window: Window<Wry>,
db_state: State<'_, Mutex<Pool<Sqlite>>>, app_handle: AppHandle,
) -> Result<GrpcRequest, String> { ) -> Result<GrpcRequest, String> {
let db = &*db_state.lock().await;
let created = upsert_grpc_request( let created = upsert_grpc_request(
db, &app_handle,
&GrpcRequest { &GrpcRequest {
workspace_id: workspace_id.to_string(), workspace_id: workspace_id.to_string(),
name: name.to_string(), name: name.to_string(),
@@ -1258,10 +1173,9 @@ async fn cmd_create_grpc_request(
async fn cmd_duplicate_grpc_request( async fn cmd_duplicate_grpc_request(
id: &str, id: &str,
window: Window<Wry>, window: Window<Wry>,
db_state: State<'_, Mutex<Pool<Sqlite>>>, app_handle: AppHandle,
) -> Result<GrpcRequest, String> { ) -> Result<GrpcRequest, String> {
let db = &*db_state.lock().await; let request = duplicate_grpc_request(&app_handle, id)
let request = duplicate_grpc_request(db, id)
.await .await
.expect("Failed to duplicate grpc request"); .expect("Failed to duplicate grpc request");
emit_and_return(&window, "created_model", request) emit_and_return(&window, "created_model", request)
@@ -1318,7 +1232,7 @@ async fn cmd_update_workspace(
.await .await
.expect("Failed to update request"); .expect("Failed to update request");
emit_and_return(&window, "updated_model", updated_workspace) emit_and_return(&window, "upserted_model", updated_workspace)
} }
#[tauri::command] #[tauri::command]
@@ -1332,20 +1246,19 @@ async fn cmd_update_environment(
.await .await
.expect("Failed to update environment"); .expect("Failed to update environment");
emit_and_return(&window, "updated_model", updated_environment) emit_and_return(&window, "upserted_model", updated_environment)
} }
#[tauri::command] #[tauri::command]
async fn cmd_update_grpc_request( async fn cmd_update_grpc_request(
request: GrpcRequest, request: GrpcRequest,
window: Window<Wry>, window: Window<Wry>,
db_state: State<'_, Mutex<Pool<Sqlite>>>, app_handle: AppHandle,
) -> Result<GrpcRequest, String> { ) -> Result<GrpcRequest, String> {
let db = &*db_state.lock().await; let updated_request = upsert_grpc_request(&app_handle, &request)
let updated_request = upsert_grpc_request(db, &request)
.await .await
.expect("Failed to update grpc request"); .expect("Failed to update grpc request");
emit_and_return(&window, "updated_model", updated_request) emit_and_return(&window, "upserted_model", updated_request)
} }
#[tauri::command] #[tauri::command]
@@ -1358,7 +1271,7 @@ async fn cmd_update_http_request(
let updated_request = upsert_http_request(db, request) let updated_request = upsert_http_request(db, request)
.await .await
.expect("Failed to update request"); .expect("Failed to update request");
emit_and_return(&window, "updated_model", updated_request) emit_and_return(&window, "upserted_model", updated_request)
} }
#[tauri::command] #[tauri::command]
@@ -1434,7 +1347,7 @@ async fn cmd_update_folder(
let updated_folder = upsert_folder(db, folder) let updated_folder = upsert_folder(db, folder)
.await .await
.expect("Failed to update request"); .expect("Failed to update request");
emit_and_return(&window, "updated_model", updated_folder) emit_and_return(&window, "upserted_model", updated_folder)
} }
#[tauri::command] #[tauri::command]
@@ -1540,7 +1453,7 @@ async fn cmd_update_settings(
.await .await
.expect("Failed to update settings"); .expect("Failed to update settings");
emit_and_return(&window, "updated_model", updated_settings) emit_and_return(&window, "upserted_model", updated_settings)
} }
#[tauri::command] #[tauri::command]
@@ -1553,12 +1466,10 @@ async fn cmd_get_folder(
} }
#[tauri::command] #[tauri::command]
async fn cmd_get_grpc_request( async fn cmd_get_grpc_request(id: &str, app_handle: AppHandle<Wry>) -> Result<GrpcRequest, String> {
id: &str, get_grpc_request(&app_handle, id)
db_state: State<'_, Mutex<Pool<Sqlite>>>, .await
) -> Result<GrpcRequest, String> { .map_err(|e| e.to_string())
let db = &*db_state.lock().await;
get_grpc_request(db, id).await.map_err(|e| e.to_string())
} }
#[tauri::command] #[tauri::command]
+50 -18
View File
@@ -7,7 +7,8 @@ use serde::{Deserialize, Serialize};
use sqlx::types::chrono::NaiveDateTime; use sqlx::types::chrono::NaiveDateTime;
use sqlx::types::{Json, JsonValue}; use sqlx::types::{Json, JsonValue};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use tauri::AppHandle; use tauri::{AppHandle, Manager};
use tokio::sync::Mutex;
fn default_true() -> bool { fn default_true() -> bool {
true true
@@ -457,18 +458,19 @@ pub async fn delete_cookie_jar(db: &Pool<Sqlite>, id: &str) -> Result<CookieJar,
} }
pub async fn duplicate_grpc_request( pub async fn duplicate_grpc_request(
db: &Pool<Sqlite>, app_handle: &AppHandle,
id: &str, id: &str,
) -> Result<GrpcRequest, sqlx::Error> { ) -> Result<GrpcRequest, sqlx::Error> {
let mut request = get_grpc_request(db, id).await?.clone(); let mut request = get_grpc_request(app_handle, id).await?.clone();
request.id = "".to_string(); request.id = "".to_string();
upsert_grpc_request(db, &request).await upsert_grpc_request(app_handle, &request).await
} }
pub async fn upsert_grpc_request( pub async fn upsert_grpc_request(
db: &Pool<Sqlite>, app_handle: &AppHandle,
request: &GrpcRequest, request: &GrpcRequest,
) -> Result<GrpcRequest, sqlx::Error> { ) -> Result<GrpcRequest, sqlx::Error> {
let db = get_db(app_handle).await;
let id = match request.id.as_str() { let id = match request.id.as_str() {
"" => generate_id(Some("gr")), "" => generate_id(Some("gr")),
_ => request.id.to_string(), _ => request.id.to_string(),
@@ -500,13 +502,19 @@ pub async fn upsert_grpc_request(
request.method, request.method,
request.message, request.message,
) )
.execute(db) .execute(&db)
.await?; .await?;
get_grpc_request(db, &id).await get_grpc_request(app_handle, &id).await
} }
pub async fn get_grpc_request(db: &Pool<Sqlite>, id: &str) -> Result<GrpcRequest, sqlx::Error> { pub async fn get_grpc_request(
app_handle: &AppHandle,
id: &str,
) -> Result<GrpcRequest, sqlx::Error> {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
sqlx::query_as!( sqlx::query_as!(
GrpcRequest, GrpcRequest,
r#" r#"
@@ -542,9 +550,10 @@ pub async fn list_grpc_requests(
} }
pub async fn upsert_grpc_connection( pub async fn upsert_grpc_connection(
db: &Pool<Sqlite>, app_handle: &AppHandle,
connection: &GrpcConnection, connection: &GrpcConnection,
) -> Result<GrpcConnection, sqlx::Error> { ) -> Result<GrpcConnection, sqlx::Error> {
let db = get_db(&app_handle).await;
let id = match connection.id.as_str() { let id = match connection.id.as_str() {
"" => generate_id(Some("gc")), "" => generate_id(Some("gc")),
_ => connection.id.to_string(), _ => connection.id.to_string(),
@@ -566,16 +575,17 @@ pub async fn upsert_grpc_connection(
connection.service, connection.service,
connection.method, connection.method,
) )
.execute(db) .execute(&db)
.await?; .await?;
get_grpc_connection(db, &id).await get_grpc_connection(app_handle, &id).await
} }
pub async fn get_grpc_connection( pub async fn get_grpc_connection(
db: &Pool<Sqlite>, app_handle: &AppHandle,
id: &str, id: &str,
) -> Result<GrpcConnection, sqlx::Error> { ) -> Result<GrpcConnection, sqlx::Error> {
let db = get_db(&app_handle).await;
sqlx::query_as!( sqlx::query_as!(
GrpcConnection, GrpcConnection,
r#" r#"
@@ -585,7 +595,7 @@ pub async fn get_grpc_connection(
"#, "#,
id, id,
) )
.fetch_one(db) .fetch_one(&db)
.await .await
} }
@@ -608,9 +618,10 @@ pub async fn list_grpc_connections(
} }
pub async fn upsert_grpc_message( pub async fn upsert_grpc_message(
db: &Pool<Sqlite>, app_handle: &AppHandle,
message: &GrpcMessage, message: &GrpcMessage,
) -> Result<GrpcMessage, sqlx::Error> { ) -> Result<GrpcMessage, sqlx::Error> {
let db = get_db(app_handle).await;
let id = match message.id.as_str() { let id = match message.id.as_str() {
"" => generate_id(Some("gm")), "" => generate_id(Some("gm")),
_ => message.id.to_string(), _ => message.id.to_string(),
@@ -635,13 +646,21 @@ pub async fn upsert_grpc_message(
message.is_server, message.is_server,
message.is_info, message.is_info,
) )
.execute(db) .execute(&db)
.await?; .await?;
get_grpc_message(db, &id).await let msg = get_grpc_message(app_handle, &id).await;
match msg {
Ok(msg) => Ok(emit_upserted_model(app_handle, msg.clone()).await),
Err(e) => Err(e),
}
} }
pub async fn get_grpc_message(db: &Pool<Sqlite>, id: &str) -> Result<GrpcMessage, sqlx::Error> { pub async fn get_grpc_message(
app_handle: &AppHandle,
id: &str,
) -> Result<GrpcMessage, sqlx::Error> {
let db = get_db(app_handle).await;
sqlx::query_as!( sqlx::query_as!(
GrpcMessage, GrpcMessage,
r#" r#"
@@ -653,7 +672,7 @@ pub async fn get_grpc_message(db: &Pool<Sqlite>, id: &str) -> Result<GrpcMessage
"#, "#,
id, id,
) )
.fetch_one(db) .fetch_one(&db)
.await .await
} }
@@ -1327,3 +1346,16 @@ pub async fn get_workspace_export_resources(
}, },
}; };
} }
async fn emit_upserted_model<S: Serialize + Clone>(app_handle: &AppHandle, model: S) -> S {
app_handle
.emit_all("upserted_model", model.clone())
.unwrap();
model
}
async fn get_db(app_handle: &AppHandle) -> Pool<Sqlite> {
let db_state = app_handle.state::<Mutex<Pool<Sqlite>>>();
let db = &*db_state.lock().await;
db.clone()
}
+15 -6
View File
@@ -86,7 +86,7 @@ export function GlobalHooks() {
} }
}); });
useListenToTauriEvent<Model>('updated_model', ({ payload, windowLabel }) => { useListenToTauriEvent<Model>('upserted_model', ({ payload, windowLabel }) => {
if (shouldIgnoreEvent(payload, windowLabel)) return; if (shouldIgnoreEvent(payload, windowLabel)) return;
const queryKey = const queryKey =
@@ -119,12 +119,21 @@ export function GlobalHooks() {
wasUpdatedExternally(payload.id); wasUpdatedExternally(payload.id);
} }
if (!shouldIgnoreModel(payload)) { const pushToFront = (['http_response', 'grpc_connection'] as Model['model'][]).includes(
console.time('set query date'); payload.model,
queryClient.setQueryData<Model[]>(queryKey, (values) =>
values?.map((v) => (modelsEq(v, payload) ? payload : v)),
); );
console.timeEnd('set query date');
if (!shouldIgnoreModel(payload)) {
queryClient.setQueryData<Model[]>(queryKey, (values = []) => {
const index = values.findIndex((v) => modelsEq(v, payload)) ?? -1;
if (index >= 0) {
console.log('UPDATED MODEL', payload);
return [...values.slice(0, index), payload, ...values.slice(index + 1)];
} else {
console.log('INSERTED MODEL', payload);
return pushToFront ? [payload, ...(values ?? [])] : [...(values ?? []), payload];
}
});
} }
}); });
+19 -4
View File
@@ -19,6 +19,7 @@ import { RadioDropdown } from './core/RadioDropdown';
import { Separator } from './core/Separator'; import { Separator } from './core/Separator';
import { SplitLayout } from './core/SplitLayout'; import { SplitLayout } from './core/SplitLayout';
import { HStack, VStack } from './core/Stacks'; import { HStack, VStack } from './core/Stacks';
import { StatusTag } from './core/StatusTag';
import { GrpcEditor } from './GrpcEditor'; import { GrpcEditor } from './GrpcEditor';
import { UrlBar } from './UrlBar'; import { UrlBar } from './UrlBar';
@@ -200,14 +201,13 @@ export function GrpcConnectionLayout({ style }: Props) {
{select.options.find((o) => o.value === select.value)?.label} {select.options.find((o) => o.value === select.value)?.label}
</Button> </Button>
</RadioDropdown> </RadioDropdown>
{!grpc.isStreaming && (
<IconButton <IconButton
className="border border-highlight" className="border border-highlight"
size="sm" size="sm"
title={messageType === 'unary' ? 'Send' : 'Connect'} title={messageType === 'unary' ? 'Send' : 'Connect'}
hotkeyAction={grpc.isStreaming ? undefined : 'http_request.send'} hotkeyAction={grpc.isStreaming ? undefined : 'http_request.send'}
onClick={grpc.isStreaming ? () => grpc.cancel.mutateAsync() : handleConnect} onClick={handleConnect}
disabled={grpc.isStreaming}
spin={grpc.isStreaming || grpc.unary.isLoading}
icon={ icon={
grpc.isStreaming grpc.isStreaming
? 'refresh' ? 'refresh'
@@ -216,6 +216,7 @@ export function GrpcConnectionLayout({ style }: Props) {
: 'arrowUpDown' : 'arrowUpDown'
} }
/> />
)}
{grpc.isStreaming && ( {grpc.isStreaming && (
<IconButton <IconButton
className="border border-highlight" className="border border-highlight"
@@ -285,7 +286,20 @@ export function GrpcConnectionLayout({ style }: Props) {
} }
minHeightPx={20} minHeightPx={20}
firstSlot={() => ( firstSlot={() => (
<div className="overflow-y-auto w-full"> <div className="w-full grid grid-rows-[auto_minmax(0,1fr)]">
<HStack className="px-3 mb-2">
<div className="font-mono">
{grpc.isStreaming ? (
<HStack alignItems="center" space={2}>
<Icon icon="refresh" size="sm" spin />
Connected
</HStack>
) : (
'Done'
)}
</div>
</HStack>
<div className="overflow-y-auto h-full">
{...messages.map((m) => ( {...messages.map((m) => (
<HStack <HStack
key={m.id} key={m.id}
@@ -319,6 +333,7 @@ export function GrpcConnectionLayout({ style }: Props) {
</HStack> </HStack>
))} ))}
</div> </div>
</div>
)} )}
secondSlot={ secondSlot={
!activeMessage !activeMessage