diff --git a/package-lock.json b/package-lock.json index 4fb940e6..13ac8a2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "yaak-app", + "name": "yaak", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "yaak-app", + "name": "yaak", "version": "0.0.0", "dependencies": { "@codemirror/commands": "^6.2.1", @@ -497,9 +497,9 @@ "integrity": "sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==" }, "node_modules/@codemirror/autocomplete": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz", - "integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.3.tgz", + "integrity": "sha512-Vl/tIeRVVUCRDuOG48lttBasNQu8usGgXQawBXI7WJAiUDSFOfzflmEsZFZo48mAvAaa4FZ/4/yLLxFtdJaKYA==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -561,9 +561,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz", + "integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -574,9 +574,9 @@ } }, "node_modules/@codemirror/lint": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.7.0.tgz", - "integrity": "sha512-LTLOL2nT41ADNSCCCCw8Q/UmdAFzB23OUYSjsHTdsVaH0XEo+orhuqbDNWzrzodm14w6FOxqxpmy4LF8Lixqjw==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.1.tgz", + "integrity": "sha512-IZ0Y7S4/bpaunwggW2jYqwLuHj0QtESf5xcROewY6+lDNwZ/NzvR4t+vpYgg9m7V8UXLPYqG+lu3DF470E5Oxg==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -599,9 +599,9 @@ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" }, "node_modules/@codemirror/view": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz", - "integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz", + "integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -7369,9 +7369,9 @@ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "node_modules/json-schema-library": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/json-schema-library/-/json-schema-library-9.3.4.tgz", - "integrity": "sha512-220lm9RVt9BUeF2QhBT711aX4IogUHhPT8Tjhkksc4CUw8WmChFMuf0mJdpDAHDfJDkI064jcZIH8P70HdPAOA==", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/json-schema-library/-/json-schema-library-9.3.5.tgz", + "integrity": "sha512-5eBDx7cbfs+RjylsVO+N36b0GOPtv78rfqgf2uON+uaHUIC62h63Y8pkV2ovKbaL4ZpQcHp21968x5nx/dFwqQ==", "dependencies": { "@sagold/json-pointer": "^5.1.2", "@sagold/json-query": "^6.1.3", diff --git a/scripts/replace-version.cjs b/scripts/replace-version.cjs index 0ac59260..f10fb21e 100644 --- a/scripts/replace-version.cjs +++ b/scripts/replace-version.cjs @@ -1,14 +1,15 @@ const path = require('path'); const fs = require('fs'); -const tauriConfigPath = path.join(__dirname, '../src-tauri/tauri.conf.json'); - -const tauriConfig = fs.readFileSync(tauriConfigPath, 'utf8'); const version = process.env.YAAK_VERSION?.replace('v', ''); if (!version) { throw new Error('YAAK_VERSION environment variable not set') } +const tauriConfigPath = path.join(__dirname, '../src-tauri/tauri.conf.json'); +const tauriConfig = JSON.parse(fs.readFileSync(tauriConfigPath, 'utf8')); + +tauriConfig.version = version; + console.log('Writing version ' + version + ' to ' + tauriConfigPath) -const newTauriConfig = tauriConfig.replaceAll('__YAAK_VERSION__', version); -fs.writeFileSync(tauriConfigPath, newTauriConfig); +fs.writeFileSync(tauriConfigPath, JSON.stringify(tauriConfig, null, 2)); diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 916414b3..80e0a64c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2290,6 +2290,7 @@ dependencies = [ "hyper 0.14.29", "hyper-rustls 0.24.2", "log", + "md5", "prost", "prost-reflect", "prost-types", @@ -3250,6 +3251,12 @@ dependencies = [ "digest", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.7.2" diff --git a/src-tauri/grpc/Cargo.toml b/src-tauri/grpc/Cargo.toml index d888c3e3..688c5abe 100644 --- a/src-tauri/grpc/Cargo.toml +++ b/src-tauri/grpc/Cargo.toml @@ -20,3 +20,4 @@ hyper-rustls = { version = "0.24.0", features = ["http2"] } uuid = { version = "1.7.0", features = ["v4"] } tauri = { version = "2.0.0-beta" } tauri-plugin-shell = "2.0.0-beta" +md5 = "0.7.0" diff --git a/src-tauri/grpc/src/manager.rs b/src-tauri/grpc/src/manager.rs index fcc3e9c5..0a7817e9 100644 --- a/src-tauri/grpc/src/manager.rs +++ b/src-tauri/grpc/src/manager.rs @@ -16,7 +16,9 @@ use tonic::transport::Uri; use tonic::{IntoRequest, IntoStreamingRequest, Request, Response, Status, Streaming}; use crate::codec::DynamicCodec; -use crate::proto::{fill_pool, fill_pool_from_files, get_transport, method_desc_to_path}; +use crate::proto::{ + fill_pool_from_files, fill_pool_from_reflection, get_transport, method_desc_to_path, +}; use crate::{json_schema, MethodDefinition, ServiceDefinition}; #[derive(Clone)] @@ -182,39 +184,36 @@ impl GrpcHandle { } impl GrpcHandle { - pub async fn services_from_files( - &mut self, - id: &str, - paths: Vec, - ) -> Result, String> { - let pool_key = format!( - "{}-{}", - id, - paths - .iter() - .map(|p| p.to_string_lossy().to_string()) - .collect::>() - .join(":") - ); - let pool = fill_pool_from_files(&self.app_handle, paths).await?; - self.pools.insert(pool_key, pool.clone()); - Ok(self.services_from_pool(&pool)) - } - - pub async fn services_from_reflection( + pub async fn reflect( &mut self, id: &str, uri: &str, - ) -> Result, String> { - // Short-circuit if no URL is set - if uri.is_empty() { - return Ok(Vec::new()); - } + proto_files: &Vec, + ) -> Result<(), String> { + let pool = if proto_files.is_empty() { + let full_uri = uri_from_str(uri)?; + fill_pool_from_reflection(&full_uri).await + } else { + fill_pool_from_files(&self.app_handle, proto_files).await + }?; - let uri = uri_from_str(uri)?; - let pool = fill_pool(&uri).await?; - let pool_key = format!("{}-{}", id, uri); - self.pools.insert(pool_key, pool.clone()); + self.pools + .insert(make_pool_key(id, uri, proto_files), pool.clone()); + Ok(()) + } + + pub async fn services( + &mut self, + id: &str, + uri: &str, + proto_files: &Vec, + ) -> Result, String> { + // Ensure reflection is up-to-date + self.reflect(id, uri, proto_files).await?; + + let pool = self + .get_pool(id, uri, proto_files) + .ok_or("Failed to get pool".to_string())?; Ok(self.services_from_pool(&pool)) } @@ -247,25 +246,26 @@ impl GrpcHandle { &mut self, id: &str, uri: &str, - proto_files: Vec, + proto_files: &Vec, ) -> Result { - let uri = uri_from_str(uri)?; - let pool = match self.pools.get(id) { - Some(p) => p.clone(), - None => match proto_files.len() { - 0 => fill_pool(&uri).await?, - _ => { - let pool = fill_pool_from_files(&self.app_handle, proto_files).await?; - self.pools.insert(id.to_string(), pool.clone()); - pool - } - }, - }; + self.reflect(id, uri, proto_files).await?; + let pool = self + .get_pool(id, uri, proto_files) + .ok_or("Failed to get pool")?; + let uri = uri_from_str(uri)?; let conn = get_transport(); - let connection = GrpcConnection { pool, conn, uri }; + let connection = GrpcConnection { + pool: pool.clone(), + conn, + uri, + }; Ok(connection) } + + fn get_pool(&self, id: &str, uri: &str, proto_files: &Vec) -> Option<&DescriptorPool> { + self.pools.get(make_pool_key(id, uri, proto_files).as_str()) + } } fn decorate_req(metadata: HashMap, req: &mut Request) -> Result<(), String> { @@ -287,3 +287,18 @@ fn uri_from_str(uri_str: &str) -> Result { } } } + +fn make_pool_key(id: &str, uri: &str, proto_files: &Vec) -> String { + let pool_key = format!( + "{}::{}::{}", + id, + uri, + proto_files + .iter() + .map(|p| p.to_string_lossy().to_string()) + .collect::>() + .join(":") + ); + + format!("{:x}", md5::compute(pool_key)) +} diff --git a/src-tauri/grpc/src/proto.rs b/src-tauri/grpc/src/proto.rs index 0260d52a..91e627c1 100644 --- a/src-tauri/grpc/src/proto.rs +++ b/src-tauri/grpc/src/proto.rs @@ -28,7 +28,7 @@ use tonic_reflection::pb::ServerReflectionRequest; pub async fn fill_pool_from_files( app_handle: &AppHandle, - paths: Vec, + paths: &Vec, ) -> Result { let mut pool = DescriptorPool::new(); let random_file_name = format!("{}.desc", uuid::Uuid::new_v4()); @@ -121,7 +121,7 @@ pub async fn fill_pool_from_files( Ok(pool) } -pub async fn fill_pool(uri: &Uri) -> Result { +pub async fn fill_pool_from_reflection(uri: &Uri) -> Result { let mut pool = DescriptorPool::new(); let mut client = ServerReflectionClient::with_origin(get_transport(), uri.clone()); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 25bca2b2..381b3e95 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -6,7 +6,7 @@ extern crate objc; use std::collections::HashMap; use std::env::current_dir; use std::fs; -use std::fs::{create_dir_all, File, read_to_string}; +use std::fs::{create_dir_all, read_to_string, File}; use std::path::PathBuf; use std::process::exit; use std::str::FromStr; @@ -17,45 +17,45 @@ use fern::colors::ColoredLevelConfig; use log::{debug, error, info, warn}; use rand::random; use serde_json::{json, Value}; -use sqlx::{Pool, Sqlite, SqlitePool}; use sqlx::migrate::Migrator; use sqlx::sqlite::SqliteConnectOptions; use sqlx::types::Json; -use tauri::{AppHandle, LogicalSize, RunEvent, State, WebviewUrl, WebviewWindow}; -use tauri::{Manager, WindowEvent}; +use sqlx::{Pool, Sqlite, SqlitePool}; use tauri::path::BaseDirectory; #[cfg(target_os = "macos")] use tauri::TitleBarStyle; +use tauri::{AppHandle, LogicalSize, RunEvent, State, WebviewUrl, WebviewWindow}; +use tauri::{Manager, WindowEvent}; use tauri_plugin_log::{fern, Target, TargetKind}; use tauri_plugin_shell::ShellExt; use tokio::sync::Mutex; -use ::grpc::{Code, deserialize_message, serialize_message, ServiceDefinition}; use ::grpc::manager::{DynamicMessage, GrpcHandle}; +use ::grpc::{deserialize_message, serialize_message, Code, ServiceDefinition}; use crate::analytics::{AnalyticsAction, AnalyticsResource}; use crate::grpc::metadata_to_map; use crate::http_request::send_http_request; use crate::models::{ - cancel_pending_grpc_connections, cancel_pending_responses, CookieJar, - create_http_response, delete_all_grpc_connections, delete_all_http_responses, delete_cookie_jar, - delete_environment, delete_folder, delete_grpc_connection, delete_grpc_request, - delete_http_request, delete_http_response, delete_workspace, duplicate_grpc_request, - duplicate_http_request, Environment, EnvironmentVariable, Folder, generate_model_id, - get_cookie_jar, get_environment, get_folder, get_grpc_connection, + cancel_pending_grpc_connections, cancel_pending_responses, create_http_response, + delete_all_grpc_connections, delete_all_http_responses, delete_cookie_jar, delete_environment, + delete_folder, delete_grpc_connection, delete_grpc_request, delete_http_request, + delete_http_response, delete_workspace, duplicate_grpc_request, duplicate_http_request, + generate_model_id, get_cookie_jar, get_environment, get_folder, get_grpc_connection, get_grpc_request, get_http_request, get_http_response, get_key_value_raw, - get_or_create_settings, get_workspace, get_workspace_export_resources, GrpcConnection, GrpcEvent, - GrpcEventType, GrpcRequest, HttpRequest, HttpResponse, KeyValue, - list_cookie_jars, list_environments, list_folders, list_grpc_connections, list_grpc_events, - list_grpc_requests, list_http_requests, list_responses, list_workspaces, ModelType, - set_key_value_raw, Settings, update_response_if_id, update_settings, upsert_cookie_jar, upsert_environment, - upsert_folder, upsert_grpc_connection, upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_workspace, Workspace, + get_or_create_settings, get_workspace, get_workspace_export_resources, list_cookie_jars, + list_environments, list_folders, list_grpc_connections, list_grpc_events, list_grpc_requests, + list_http_requests, list_responses, list_workspaces, set_key_value_raw, update_response_if_id, + update_settings, upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection, + upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_workspace, CookieJar, + Environment, EnvironmentVariable, Folder, GrpcConnection, GrpcEvent, GrpcEventType, + GrpcRequest, HttpRequest, HttpResponse, KeyValue, ModelType, Settings, Workspace, WorkspaceExportResources, }; use crate::notifications::YaakNotifier; use crate::plugin::{ - find_plugins, get_plugin, ImportResult, PluginCapability, - run_plugin_export_curl, run_plugin_filter, run_plugin_import, + find_plugins, get_plugin, run_plugin_export_curl, run_plugin_filter, run_plugin_import, + ImportResult, PluginCapability, }; use crate::render::{render_request, variables_from_environment}; use crate::updates::{UpdateMode, YaakUpdater}; @@ -135,26 +135,18 @@ async fn cmd_grpc_reflect( .await .map_err(|e| e.to_string())?; - let uri = safe_uri(req.url.as_str()); - if proto_files.len() > 0 { - grpc_handle - .lock() - .await - .services_from_files( - &req.id, - proto_files - .iter() - .map(|p| PathBuf::from_str(p).unwrap()) - .collect(), - ) - .await - } else { - grpc_handle - .lock() - .await - .services_from_reflection(&req.id, uri.as_str()) - .await - } + grpc_handle + .lock() + .await + .services( + &req.id, + &req.url, + &proto_files + .iter() + .map(|p| PathBuf::from_str(p).unwrap()) + .collect(), + ) + .await } #[tauri::command] @@ -231,6 +223,7 @@ async fn cmd_grpc_go( workspace_id: req.workspace_id, request_id: req.id, status: -1, + elapsed: 0, url: req.url.clone(), ..Default::default() }, @@ -238,6 +231,7 @@ async fn cmd_grpc_go( .await .map_err(|e| e.to_string())? }; + let conn_id = conn.id.clone(); let base_msg = GrpcEvent { @@ -270,12 +264,29 @@ async fn cmd_grpc_go( .connect( &req.clone().id, uri.as_str(), - proto_files + &proto_files .iter() .map(|p| PathBuf::from_str(p).unwrap()) .collect(), ) - .await?; + .await; + + let connection = match connection { + Ok(c) => c, + Err(err) => { + upsert_grpc_connection( + &w, + &GrpcConnection { + elapsed: start.elapsed().as_millis() as i64, + error: Some(err.clone()), + ..conn.clone() + }, + ) + .await + .map_err(|e| e.to_string())?; + return Ok(conn_id); + } + }; let method_desc = connection .method(&service, &method) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e81fe862..99fa51dd 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "yaak", - "version": "__YAAK_VERSION__", + "version": "0.0.0", "identifier": "app.yaak.desktop", "build": { "beforeBuildCommand": "npm run build", diff --git a/src-web/components/GrpcConnectionLayout.tsx b/src-web/components/GrpcConnectionLayout.tsx index f777306b..f0a13181 100644 --- a/src-web/components/GrpcConnectionLayout.tsx +++ b/src-web/components/GrpcConnectionLayout.tsx @@ -86,6 +86,7 @@ export function GrpcConnectionLayout({ style }: Props) { activeRequest={activeRequest} protoFiles={protoFiles} methodType={methodType} + isStreaming={grpc.isStreaming} onGo={grpc.go.mutate} onCommit={grpc.commit.mutate} onCancel={grpc.cancel.mutate} diff --git a/src-web/components/GrpcConnectionMessagesPane.tsx b/src-web/components/GrpcConnectionMessagesPane.tsx index 66548bbc..3d3bbe98 100644 --- a/src-web/components/GrpcConnectionMessagesPane.tsx +++ b/src-web/components/GrpcConnectionMessagesPane.tsx @@ -6,6 +6,8 @@ import { useGrpcEvents } from '../hooks/useGrpcEvents'; import { usePinnedGrpcConnection } from '../hooks/usePinnedGrpcConnection'; import { useStateWithDeps } from '../hooks/useStateWithDeps'; import type { GrpcEvent, GrpcRequest } from '../lib/models'; +import { isResponseLoading } from '../lib/models'; +import { Banner } from './core/Banner'; import { Button } from './core/Button'; import { Icon } from './core/Icon'; import { JsonAttributeTree } from './core/JsonAttributeTree'; @@ -64,7 +66,7 @@ export function GrpcConnectionMessagesPane({ style, methodType, activeRequest }: {events.length} messages - {activeConnection.elapsed === 0 && ( + {isResponseLoading(activeConnection) && ( )} @@ -75,6 +77,11 @@ export function GrpcConnectionMessagesPane({ style, methodType, activeRequest }: />
+ {activeConnection.error && ( + + {activeConnection.error} + + )} {...events.map((e) => ( void; onCancel: () => void; onSend: (v: { message: string }) => void; @@ -54,15 +54,13 @@ export function GrpcConnectionSetupPane({ protoFiles, reflectionError, reflectionLoading, + isStreaming, onGo, onCommit, onCancel, onSend, }: Props) { - const connections = useGrpcConnections(activeRequest.id ?? null); const updateRequest = useUpdateGrpcRequest(activeRequest?.id ?? null); - const activeConnection = connections[0] ?? null; - const isStreaming = activeConnection?.elapsed === 0; const [activeTab, setActiveTab] = useActiveTab(); const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null); @@ -78,7 +76,9 @@ export function GrpcConnectionSetupPane({ ); const handleChangeMessage = useCallback( - (message: string) => updateRequest.mutateAsync({ message }), + (message: string) => { + return updateRequest.mutateAsync({ message }); + }, [updateRequest], ); diff --git a/src-web/components/GrpcEditor.tsx b/src-web/components/GrpcEditor.tsx index 97a0c419..fbbc7ec3 100644 --- a/src-web/components/GrpcEditor.tsx +++ b/src-web/components/GrpcEditor.tsx @@ -6,8 +6,8 @@ import { handleRefresh, jsonCompletion, jsonSchemaLinter, - stateExtensions, updateSchema, + stateExtensions, } from 'codemirror-json-schema'; import { useEffect, useMemo, useRef } from 'react'; import { useAlert } from '../hooks/useAlert'; @@ -41,6 +41,7 @@ export function GrpcEditor({ ...extraEditorProps }: Props) { const editorViewRef = useRef(null); + const alert = useAlert(); const dialog = useDialog(); diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index 3a52b863..ef5c4f15 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -838,7 +838,7 @@ const SidebarItem = forwardRef(function SidebarItem(
{latestGrpcConnection ? (
- {latestGrpcConnection.elapsed === 0 && ( + {isResponseLoading(latestGrpcConnection) && ( )}
diff --git a/src-web/hooks/useGrpc.ts b/src-web/hooks/useGrpc.ts index 4d755b71..eafd6003 100644 --- a/src-web/hooks/useGrpc.ts +++ b/src-web/hooks/useGrpc.ts @@ -44,11 +44,12 @@ export function useGrpc( onSettled: () => trackEvent('grpc_connection', 'commit'), }); - const debouncedUrl = useDebouncedValue(req?.url ?? 'n/a', 500); + const debouncedUrl = useDebouncedValue(req?.url ?? '', 1000); + const debouncedMessage = useDebouncedValue(req?.message ?? '', 1000); + const reflect = useQuery({ enabled: req != null, - queryKey: ['grpc_reflect', req?.id ?? 'n/a', debouncedUrl, protoFiles], - refetchOnWindowFocus: false, + queryKey: ['grpc_reflect', req?.id ?? 'n/a', debouncedUrl, debouncedMessage, protoFiles], queryFn: async () => (await minPromiseMillis( invokeCmd('cmd_grpc_reflect', { requestId, protoFiles }), @@ -56,12 +57,13 @@ export function useGrpc( )) as ReflectResponseService[], }); + console.log('CONN', conn); return { go, reflect, cancel, commit, - isStreaming: conn?.elapsed === 0, + isStreaming: conn != null && conn.elapsed === 0, send, }; } diff --git a/src-web/lib/models.ts b/src-web/lib/models.ts index 27048d9a..afe4df5e 100644 --- a/src-web/lib/models.ts +++ b/src-web/lib/models.ts @@ -207,7 +207,7 @@ export interface HttpResponse extends BaseModel { readonly headers: HttpHeader[]; } -export function isResponseLoading(response: HttpResponse): boolean { +export function isResponseLoading(response: HttpResponse | GrpcConnection): boolean { return response.elapsed === 0; }