mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-22 17:39:12 +01:00
Proto selection UI/models
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message\n FROM grpc_requests\n WHERE workspace_id = ?\n ",
|
||||
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message,\n proto_files AS \"proto_files!: sqlx::types::Json<Vec<String>>\"\n FROM grpc_requests\n WHERE id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -62,6 +62,11 @@
|
||||
"name": "message",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "proto_files!: sqlx::types::Json<Vec<String>>",
|
||||
"ordinal": 12,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -79,8 +84,9 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "35c9607291ee400e7696393bec7f4aa254a2dd163c4f79ea368ac6ee5f74c365"
|
||||
"hash": "7398403d3de2dc5c5b4b6392f083041d9a55194bb97819225a2612fdeb60ad42"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message\n FROM grpc_requests\n WHERE id = ?\n ",
|
||||
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message,\n proto_files AS \"proto_files!: sqlx::types::Json<Vec<String>>\"\n FROM grpc_requests\n WHERE workspace_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -62,6 +62,11 @@
|
||||
"name": "message",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "proto_files!: sqlx::types::Json<Vec<String>>",
|
||||
"ordinal": 12,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -79,8 +84,9 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c5f9b12bca35fe65ae2a1625e7f09c7855ce107046113a45f6da2abcff41819f"
|
||||
"hash": "761d27c3ec425c37ad9abe9c732a9c1746c81ca50d2c413e540b74c8c8e908b7"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO grpc_requests (\n id, name, workspace_id, folder_id, sort_priority, url, service, method, message\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n folder_id = excluded.folder_id,\n sort_priority = excluded.sort_priority,\n url = excluded.url,\n service = excluded.service,\n method = excluded.method,\n message = excluded.message\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 9
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d2dc9a652fe08623d70b8b0a2e6823d096e8cbcb9f06544ab245f41425335a25"
|
||||
}
|
||||
12
src-tauri/.sqlx/query-ee562f85ec28c554c607adde670fc30eaeffeed6883e712bda4b4d6ca49cf740.json
generated
Normal file
12
src-tauri/.sqlx/query-ee562f85ec28c554c607adde670fc30eaeffeed6883e712bda4b4d6ca49cf740.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO grpc_requests (\n id, name, workspace_id, folder_id, sort_priority, url, service, method, message,\n proto_files\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n folder_id = excluded.folder_id,\n sort_priority = excluded.sort_priority,\n url = excluded.url,\n service = excluded.service,\n method = excluded.method,\n message = excluded.message,\n proto_files = excluded.proto_files\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 10
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ee562f85ec28c554c607adde670fc30eaeffeed6883e712bda4b4d6ca49cf740"
|
||||
}
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
tonic = "0.10.2"
|
||||
prost = "0.12"
|
||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "fs"] }
|
||||
tonic-reflection = "0.10.2"
|
||||
tokio-stream = "0.1.14"
|
||||
prost-types = "0.12.3"
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use prost_reflect::SerializeOptions;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tonic::transport::Uri;
|
||||
|
||||
use crate::proto::fill_pool;
|
||||
|
||||
mod codec;
|
||||
mod json_schema;
|
||||
@@ -28,31 +25,3 @@ pub struct MethodDefinition {
|
||||
pub client_streaming: bool,
|
||||
pub server_streaming: bool,
|
||||
}
|
||||
|
||||
pub async fn reflect(uri: &Uri) -> Result<Vec<ServiceDefinition>, String> {
|
||||
let (pool, _) = fill_pool(uri).await?;
|
||||
|
||||
Ok(pool
|
||||
.services()
|
||||
.map(|s| {
|
||||
let mut def = ServiceDefinition {
|
||||
name: s.full_name().to_string(),
|
||||
methods: vec![],
|
||||
};
|
||||
for method in s.methods() {
|
||||
let input_message = method.input();
|
||||
def.methods.push(MethodDefinition {
|
||||
name: method.name().to_string(),
|
||||
server_streaming: method.is_server_streaming(),
|
||||
client_streaming: method.is_client_streaming(),
|
||||
schema: serde_json::to_string_pretty(&json_schema::message_to_json_schema(
|
||||
&pool,
|
||||
input_message,
|
||||
))
|
||||
.unwrap(),
|
||||
})
|
||||
}
|
||||
def
|
||||
})
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use hyper_rustls::HttpsConnector;
|
||||
use prost_reflect::DescriptorPool;
|
||||
pub use prost_reflect::DynamicMessage;
|
||||
use serde_json::Deserializer;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
use tokio_stream::StreamExt;
|
||||
use tonic::body::BoxBody;
|
||||
@@ -15,6 +14,7 @@ use tonic::{IntoRequest, IntoStreamingRequest, Streaming};
|
||||
|
||||
use crate::codec::DynamicCodec;
|
||||
use crate::proto::{fill_pool, method_desc_to_path};
|
||||
use crate::{json_schema, MethodDefinition, ServiceDefinition};
|
||||
|
||||
type Result<T> = std::result::Result<T, String>;
|
||||
|
||||
@@ -145,25 +145,61 @@ impl GrpcConnection {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GrpcManager {
|
||||
pub struct GrpcHandle {
|
||||
connections: HashMap<String, GrpcConnection>,
|
||||
pub send: mpsc::Sender<String>,
|
||||
pub recv: mpsc::Receiver<String>,
|
||||
pools: HashMap<String, DescriptorPool>,
|
||||
}
|
||||
|
||||
impl Default for GrpcManager {
|
||||
impl Default for GrpcHandle {
|
||||
fn default() -> Self {
|
||||
let (send, recv) = mpsc::channel(100);
|
||||
let connections = HashMap::new();
|
||||
Self {
|
||||
connections,
|
||||
send,
|
||||
recv,
|
||||
}
|
||||
let pools = HashMap::new();
|
||||
Self { connections, pools }
|
||||
}
|
||||
}
|
||||
|
||||
impl GrpcManager {
|
||||
impl GrpcHandle {
|
||||
pub async fn clean_reflect(&mut self, uri: &Uri) -> Result<Vec<ServiceDefinition>> {
|
||||
self.pools.remove(&uri.to_string());
|
||||
self.reflect(uri).await
|
||||
}
|
||||
|
||||
pub async fn reflect(&mut self, uri: &Uri) -> Result<Vec<ServiceDefinition>> {
|
||||
let pool = match self.pools.get(&uri.to_string()) {
|
||||
Some(p) => p.clone(),
|
||||
None => {
|
||||
let (pool, _) = fill_pool(uri).await?;
|
||||
self.pools.insert(uri.to_string(), pool.clone());
|
||||
pool
|
||||
}
|
||||
};
|
||||
|
||||
let result =
|
||||
pool.services()
|
||||
.map(|s| {
|
||||
let mut def = ServiceDefinition {
|
||||
name: s.full_name().to_string(),
|
||||
methods: vec![],
|
||||
};
|
||||
for method in s.methods() {
|
||||
let input_message = method.input();
|
||||
def.methods.push(MethodDefinition {
|
||||
name: method.name().to_string(),
|
||||
server_streaming: method.is_server_streaming(),
|
||||
client_streaming: method.is_client_streaming(),
|
||||
schema: serde_json::to_string_pretty(
|
||||
&json_schema::message_to_json_schema(&pool, input_message),
|
||||
)
|
||||
.unwrap(),
|
||||
})
|
||||
}
|
||||
def
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn server_streaming(
|
||||
&mut self,
|
||||
id: &str,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
@@ -9,17 +10,27 @@ use log::warn;
|
||||
use prost::Message;
|
||||
use prost_reflect::{DescriptorPool, MethodDescriptor};
|
||||
use prost_types::FileDescriptorProto;
|
||||
use tokio::fs;
|
||||
use tokio_stream::StreamExt;
|
||||
use tonic::body::BoxBody;
|
||||
use tonic::codegen::http::uri::PathAndQuery;
|
||||
use tonic::transport::Uri;
|
||||
use tonic::Code::Unimplemented;
|
||||
use tonic::Request;
|
||||
use tonic_reflection::pb::server_reflection_client::ServerReflectionClient;
|
||||
use tonic_reflection::pb::server_reflection_request::MessageRequest;
|
||||
use tonic_reflection::pb::server_reflection_response::MessageResponse;
|
||||
use tonic_reflection::pb::ServerReflectionRequest;
|
||||
|
||||
pub async fn fill_pool_from_files(paths: Vec<PathBuf>) -> Result<DescriptorPool, String> {
|
||||
let mut pool = DescriptorPool::new();
|
||||
for p in paths {
|
||||
let bytes = fs::read(p).await.unwrap();
|
||||
let fdp = FileDescriptorProto::decode(bytes.deref()).unwrap();
|
||||
pool.add_file_descriptor_proto(fdp)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
Ok(pool)
|
||||
}
|
||||
pub async fn fill_pool(
|
||||
uri: &Uri,
|
||||
) -> Result<
|
||||
@@ -155,7 +166,9 @@ async fn send_reflection_request(
|
||||
.server_reflection_info(request)
|
||||
.await
|
||||
.map_err(|e| match e.code() {
|
||||
Unimplemented => "Reflection not implemented for server".to_string(),
|
||||
tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(),
|
||||
tonic::Code::Unauthenticated => "Authentication failed".to_string(),
|
||||
tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(),
|
||||
_ => e.to_string(),
|
||||
})?
|
||||
.into_inner()
|
||||
|
||||
1
src-tauri/migrations/20240206191206_grpc-protos.sql
Normal file
1
src-tauri/migrations/20240206191206_grpc-protos.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE grpc_requests ADD COLUMN proto_files TEXT DEFAULT '[]' NOT NULL;
|
||||
@@ -34,7 +34,7 @@ use tokio::sync::Mutex;
|
||||
use tokio::time::sleep;
|
||||
use window_shadows::set_shadow;
|
||||
|
||||
use grpc::manager::GrpcManager;
|
||||
use grpc::manager::GrpcHandle;
|
||||
use grpc::ServiceDefinition;
|
||||
use window_ext::TrafficLightWindowExt;
|
||||
|
||||
@@ -95,19 +95,20 @@ async fn migrate_db(app_handle: AppHandle, db: &Mutex<Pool<Sqlite>>) -> Result<(
|
||||
async fn cmd_grpc_reflect(
|
||||
request_id: &str,
|
||||
app_handle: AppHandle,
|
||||
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
||||
) -> Result<Vec<ServiceDefinition>, String> {
|
||||
let req = get_grpc_request(&app_handle, request_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let uri = safe_uri(&req.url).map_err(|e| e.to_string())?;
|
||||
grpc::reflect(&uri).await
|
||||
grpc_handle.lock().await.clean_reflect(&uri).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_grpc_call_unary(
|
||||
request_id: &str,
|
||||
app_handle: AppHandle,
|
||||
grpc_handle: State<'_, Mutex<GrpcManager>>,
|
||||
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
||||
) -> Result<GrpcMessage, String> {
|
||||
let req = get_grpc_request(&app_handle, request_id)
|
||||
.await
|
||||
@@ -316,7 +317,7 @@ async fn cmd_grpc_client_streaming(
|
||||
let conn = conn.clone();
|
||||
let req = req.clone();
|
||||
async move {
|
||||
let grpc_handle = app_handle.state::<Mutex<GrpcManager>>();
|
||||
let grpc_handle = app_handle.state::<Mutex<GrpcHandle>>();
|
||||
let msg = grpc_handle
|
||||
.lock()
|
||||
.await
|
||||
@@ -404,7 +405,7 @@ async fn cmd_grpc_client_streaming(
|
||||
async fn cmd_grpc_streaming(
|
||||
request_id: &str,
|
||||
app_handle: AppHandle,
|
||||
grpc_handle: State<'_, Mutex<GrpcManager>>,
|
||||
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
||||
) -> Result<String, String> {
|
||||
let req = get_grpc_request(&app_handle, request_id)
|
||||
.await
|
||||
@@ -614,7 +615,7 @@ async fn cmd_grpc_streaming(
|
||||
async fn cmd_grpc_server_streaming(
|
||||
request_id: &str,
|
||||
app_handle: AppHandle,
|
||||
grpc_handle: State<'_, Mutex<GrpcManager>>,
|
||||
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
||||
) -> Result<GrpcConnection, String> {
|
||||
let req = get_grpc_request(&app_handle, request_id)
|
||||
.await
|
||||
@@ -1627,7 +1628,7 @@ fn main() {
|
||||
app.manage(Mutex::new(yaak_updater));
|
||||
|
||||
// Add GRPC manager
|
||||
let grpc_handle = GrpcManager::default();
|
||||
let grpc_handle = GrpcHandle::default();
|
||||
app.manage(Mutex::new(grpc_handle));
|
||||
|
||||
// Add DB handle
|
||||
|
||||
@@ -204,6 +204,7 @@ pub struct GrpcRequest {
|
||||
pub service: Option<String>,
|
||||
pub method: Option<String>,
|
||||
pub message: String,
|
||||
pub proto_files: Json<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)]
|
||||
@@ -497,9 +498,10 @@ pub async fn upsert_grpc_request(
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO grpc_requests (
|
||||
id, name, workspace_id, folder_id, sort_priority, url, service, method, message
|
||||
id, name, workspace_id, folder_id, sort_priority, url, service, method, message,
|
||||
proto_files
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
updated_at = CURRENT_TIMESTAMP,
|
||||
name = excluded.name,
|
||||
@@ -508,7 +510,8 @@ pub async fn upsert_grpc_request(
|
||||
url = excluded.url,
|
||||
service = excluded.service,
|
||||
method = excluded.method,
|
||||
message = excluded.message
|
||||
message = excluded.message,
|
||||
proto_files = excluded.proto_files
|
||||
"#,
|
||||
id,
|
||||
trimmed_name,
|
||||
@@ -519,6 +522,7 @@ pub async fn upsert_grpc_request(
|
||||
request.service,
|
||||
request.method,
|
||||
request.message,
|
||||
request.proto_files,
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -539,7 +543,8 @@ pub async fn get_grpc_request(
|
||||
r#"
|
||||
SELECT
|
||||
id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,
|
||||
url, service, method, message
|
||||
url, service, method, message,
|
||||
proto_files AS "proto_files!: sqlx::types::Json<Vec<String>>"
|
||||
FROM grpc_requests
|
||||
WHERE id = ?
|
||||
"#,
|
||||
@@ -559,7 +564,8 @@ pub async fn list_grpc_requests(
|
||||
r#"
|
||||
SELECT
|
||||
id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,
|
||||
url, service, method, message
|
||||
url, service, method, message,
|
||||
proto_files AS "proto_files!: sqlx::types::Json<Vec<String>>"
|
||||
FROM grpc_requests
|
||||
WHERE workspace_id = ?
|
||||
"#,
|
||||
|
||||
Reference in New Issue
Block a user