From 7bc26fd4485953d9c3ecbb3a7f18d7e75524f04e Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Wed, 7 Feb 2024 00:02:02 -0800 Subject: [PATCH] Working sidebar actions for grpc --- src-tauri/grpc/src/proto.rs | 30 ++++++------- src-tauri/src/main.rs | 26 +++++------ src-tauri/src/models.rs | 20 +++++++++ .../components/GrpcConnectionMessagesPane.tsx | 24 +++++++++-- src-web/components/Sidebar.tsx | 34 +++++++++------ src-web/hooks/Confirm.tsx | 2 +- src-web/hooks/useDeleteAnyGrpcRequest.tsx | 43 +++++++++++++++++++ ...equest.tsx => useDeleteAnyHttpRequest.tsx} | 4 +- src-web/hooks/useDeleteRequest.tsx | 4 +- 9 files changed, 137 insertions(+), 50 deletions(-) create mode 100644 src-web/hooks/useDeleteAnyGrpcRequest.tsx rename src-web/hooks/{useDeleteAnyRequest.tsx => useDeleteAnyHttpRequest.tsx} (95%) diff --git a/src-tauri/grpc/src/proto.rs b/src-tauri/grpc/src/proto.rs index 00f802d2..5fde08d4 100644 --- a/src-tauri/grpc/src/proto.rs +++ b/src-tauri/grpc/src/proto.rs @@ -24,7 +24,6 @@ use tonic_reflection::pb::server_reflection_response::MessageResponse; use tonic_reflection::pb::ServerReflectionRequest; pub async fn fill_pool_from_files(paths: Vec) -> Result { - println!("FILL POOL FROM FILES"); let mut pool = DescriptorPool::new(); let random_file_name = format!("{}.desc", uuid::Uuid::new_v4()); let desc_path = temp_dir().join(random_file_name); @@ -51,8 +50,6 @@ pub async fn fill_pool_from_files(paths: Vec) -> Result) -> Result Client, BoxBody> { - let connector = HttpsConnectorBuilder::new().with_native_roots(); - let connector = connector.https_or_http().enable_http2().wrap_connector({ - let mut http_connector = HttpConnector::new(); - http_connector.enforce_http(false); - http_connector - }); - Client::builder() - .pool_max_idle_per_host(0) - .http2_only(true) - .build(connector) -} - pub async fn fill_pool(uri: &Uri) -> Result { - println!("FILL POOL FROM URI"); let mut pool = DescriptorPool::new(); let mut client = ServerReflectionClient::with_origin(get_transport(), uri.clone()); @@ -103,6 +86,19 @@ pub async fn fill_pool(uri: &Uri) -> Result { Ok(pool) } +pub fn get_transport() -> Client, BoxBody> { + let connector = HttpsConnectorBuilder::new().with_native_roots(); + let connector = connector.https_or_http().enable_http2().wrap_connector({ + let mut http_connector = HttpConnector::new(); + http_connector.enforce_http(false); + http_connector + }); + Client::builder() + .pool_max_idle_per_host(0) + .http2_only(true) + .build(connector) +} + async fn list_services( reflect_client: &mut ServerReflectionClient, BoxBody>>, ) -> Result, String> { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 699959e9..998bf07f 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -44,16 +44,17 @@ use crate::http::send_http_request; use crate::models::{ cancel_pending_grpc_connections, cancel_pending_responses, create_response, delete_all_grpc_connections, delete_all_http_responses, delete_cookie_jar, delete_environment, - delete_folder, delete_grpc_connection, delete_http_request, delete_http_response, - delete_workspace, duplicate_grpc_request, duplicate_http_request, get_cookie_jar, - get_environment, get_folder, get_grpc_request, get_http_request, get_http_response, - get_key_value_raw, get_or_create_settings, get_workspace, get_workspace_export_resources, - list_cookie_jars, list_environments, list_folders, list_grpc_connections, list_grpc_messages, - list_grpc_requests, list_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_message, upsert_grpc_request, upsert_http_request, - upsert_workspace, CookieJar, Environment, EnvironmentVariable, Folder, GrpcConnection, - GrpcMessage, GrpcRequest, HttpRequest, HttpResponse, KeyValue, Settings, Workspace, + delete_folder, delete_grpc_connection, delete_grpc_request, delete_http_request, + delete_http_response, delete_workspace, duplicate_grpc_request, duplicate_http_request, + get_cookie_jar, get_environment, get_folder, get_grpc_request, get_http_request, + get_http_response, get_key_value_raw, get_or_create_settings, get_workspace, + get_workspace_export_resources, list_cookie_jars, list_environments, list_folders, + list_grpc_connections, list_grpc_messages, list_grpc_requests, list_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_message, + upsert_grpc_request, upsert_http_request, upsert_workspace, CookieJar, Environment, + EnvironmentVariable, Folder, GrpcConnection, GrpcMessage, GrpcRequest, HttpRequest, + HttpResponse, KeyValue, Settings, Workspace, }; use crate::plugin::{ImportResources, ImportResult}; use crate::updates::{update_mode_from_str, UpdateMode, YaakUpdater}; @@ -103,7 +104,6 @@ async fn cmd_grpc_reflect( .map_err(|e| e.to_string())?; let uri = safe_uri(&req.url).map_err(|e| e.to_string())?; if req.proto_files.0.len() > 0 { - println!("REFLECT FROM FILES"); grpc_handle .lock() .await @@ -1331,8 +1331,8 @@ async fn cmd_update_http_request( async fn cmd_delete_grpc_request( app_handle: AppHandle, request_id: &str, -) -> Result { - delete_http_request(&app_handle, request_id) +) -> Result { + delete_grpc_request(&app_handle, request_id) .await .map_err(|e| e.to_string()) } diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index d288a387..5620ca7b 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -1379,6 +1379,26 @@ pub async fn list_responses_by_workspace_id( .await } +pub async fn delete_grpc_request( + app_handle: &AppHandle, + id: &str, +) -> Result { + let req = get_grpc_request(app_handle, id).await?; + + let db = get_db(app_handle).await; + let _ = sqlx::query!( + r#" + DELETE FROM grpc_requests + WHERE id = ? + "#, + id, + ) + .execute(&db) + .await; + + emit_deleted_model(app_handle, req) +} + pub async fn delete_grpc_connection( app_handle: &AppHandle, id: &str, diff --git a/src-web/components/GrpcConnectionMessagesPane.tsx b/src-web/components/GrpcConnectionMessagesPane.tsx index 6547c79a..3efc19a0 100644 --- a/src-web/components/GrpcConnectionMessagesPane.tsx +++ b/src-web/components/GrpcConnectionMessagesPane.tsx @@ -65,6 +65,7 @@ export function GrpcConnectionMessagesPane({ style, methodType, activeRequest }:
{...messages.map((m) => ( { @@ -72,7 +73,10 @@ export function GrpcConnectionMessagesPane({ style, methodType, activeRequest }: else setActiveMessageId(m.id); }} alignItems="center" - className={classNames('px-2 py-1 font-mono', m === activeMessage && 'bg-highlight')} + className={classNames( + 'px-2 py-1 font-mono cursor-default group', + m === activeMessage && '!bg-highlight', + )} > -
{m.message}
-
{format(m.createdAt, 'HH:mm:ss')}
+
+ {m.message} +
+
+ {format(m.createdAt, 'HH:mm:ss')} +
))}
diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index dfe12338..ee21e96e 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -11,7 +11,8 @@ import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; import { useAppRoutes } from '../hooks/useAppRoutes'; import { useCreateFolder } from '../hooks/useCreateFolder'; import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest'; -import { useDeleteAnyRequest } from '../hooks/useDeleteAnyRequest'; +import { useDeleteAnyGrpcRequest } from '../hooks/useDeleteAnyGrpcRequest'; +import { useDeleteAnyHttpRequest } from '../hooks/useDeleteAnyHttpRequest'; import { useDeleteFolder } from '../hooks/useDeleteFolder'; import { useDeleteRequest } from '../hooks/useDeleteRequest'; import { useDuplicateGrpcRequest } from '../hooks/useDuplicateGrpcRequest'; @@ -36,6 +37,7 @@ import { fallbackRequestName } from '../lib/fallbackRequestName'; import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore'; import type { Folder, GrpcRequest, HttpRequest, Workspace } from '../lib/models'; import { isResponseLoading } from '../lib/models'; +import type { DropdownItem } from './core/Dropdown'; import { ContextMenu } from './core/Dropdown'; import { Icon } from './core/Icon'; import { InlineCode } from './core/InlineCode'; @@ -65,7 +67,8 @@ export function Sidebar({ className }: Props) { const httpRequests = useHttpRequests(); const grpcRequests = useGrpcRequests(); const folders = useFolders(); - const deleteAnyRequest = useDeleteAnyRequest(); + const deleteAnyHttpRequest = useDeleteAnyHttpRequest(); + const deleteAnyGrpcRequest = useDeleteAnyGrpcRequest(); const activeWorkspace = useActiveWorkspace(); const duplicateHttpRequest = useDuplicateHttpRequest({ id: activeRequest?.id ?? null, @@ -223,9 +226,10 @@ export function Sidebar({ className }: Props) { const selected = selectableRequests.find((r) => r.id === selectedId); if (selected == null) return; - deleteAnyRequest.mutate(selected.id); + deleteAnyHttpRequest.mutate(selected.id); + deleteAnyGrpcRequest.mutate(selected.id); }, - [deleteAnyRequest, hasFocus, selectableRequests, selectedId], + [deleteAnyHttpRequest, deleteAnyGrpcRequest, hasFocus, selectableRequests, selectedId], ); useKeyPressEvent('Backspace', handleDeleteKey); @@ -683,15 +687,19 @@ const SidebarItem = forwardRef(function SidebarItem( }, ] : [ - { - key: 'sendRequest', - label: 'Send', - hotKeyAction: 'http_request.send', - hotKeyLabelOnly: true, // Already bound in URL bar - leftSlot: , - onSelect: () => sendRequest.mutate(), - }, - { type: 'separator' }, + ...((itemModel === 'http_request' + ? [ + { + key: 'sendRequest', + label: 'Send', + hotKeyAction: 'http_request.send', + hotKeyLabelOnly: true, // Already bound in URL bar + leftSlot: , + onSelect: () => sendRequest.mutate(), + }, + { type: 'separator' }, + ] + : []) as DropdownItem[]), { key: 'duplicateRequest', label: 'Duplicate', diff --git a/src-web/hooks/Confirm.tsx b/src-web/hooks/Confirm.tsx index 255e1316..fdf2c594 100644 --- a/src-web/hooks/Confirm.tsx +++ b/src-web/hooks/Confirm.tsx @@ -30,7 +30,7 @@ export function Confirm({ onHide, onResult, variant = 'confirm' }: ConfirmProps) }; return ( - + diff --git a/src-web/hooks/useDeleteAnyGrpcRequest.tsx b/src-web/hooks/useDeleteAnyGrpcRequest.tsx new file mode 100644 index 00000000..d824605c --- /dev/null +++ b/src-web/hooks/useDeleteAnyGrpcRequest.tsx @@ -0,0 +1,43 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { invoke } from '@tauri-apps/api'; +import { InlineCode } from '../components/core/InlineCode'; +import { trackEvent } from '../lib/analytics'; +import { fallbackRequestName } from '../lib/fallbackRequestName'; +import type { GrpcRequest } from '../lib/models'; +import { getGrpcRequest } from '../lib/store'; +import { useConfirm } from './useConfirm'; +import { grpcRequestsQueryKey } from './useGrpcRequests'; + +export function useDeleteAnyGrpcRequest() { + const queryClient = useQueryClient(); + const confirm = useConfirm(); + + return useMutation({ + mutationFn: async (id) => { + const request = await getGrpcRequest(id); + if (request == null) return null; + + const confirmed = await confirm({ + id: 'delete-grpc-request', + title: 'Delete Request', + variant: 'delete', + description: ( + <> + Permanently delete {fallbackRequestName(request)}? + + ), + }); + if (!confirmed) return null; + return invoke('cmd_delete_grpc_request', { requestId: id }); + }, + onSettled: () => trackEvent('GrpcRequest', 'Delete'), + onSuccess: async (request) => { + if (request === null) return; + + const { workspaceId, id: requestId } = request; + queryClient.setQueryData(grpcRequestsQueryKey({ workspaceId }), (requests) => + (requests ?? []).filter((r) => r.id !== requestId), + ); + }, + }); +} diff --git a/src-web/hooks/useDeleteAnyRequest.tsx b/src-web/hooks/useDeleteAnyHttpRequest.tsx similarity index 95% rename from src-web/hooks/useDeleteAnyRequest.tsx rename to src-web/hooks/useDeleteAnyHttpRequest.tsx index af54cf25..de3af626 100644 --- a/src-web/hooks/useDeleteAnyRequest.tsx +++ b/src-web/hooks/useDeleteAnyHttpRequest.tsx @@ -9,13 +9,15 @@ import { useConfirm } from './useConfirm'; import { httpRequestsQueryKey } from './useHttpRequests'; import { httpResponsesQueryKey } from './useHttpResponses'; -export function useDeleteAnyRequest() { +export function useDeleteAnyHttpRequest() { const queryClient = useQueryClient(); const confirm = useConfirm(); return useMutation({ mutationFn: async (id) => { const request = await getHttpRequest(id); + if (request == null) return null; + const confirmed = await confirm({ id: 'delete-request', title: 'Delete Request', diff --git a/src-web/hooks/useDeleteRequest.tsx b/src-web/hooks/useDeleteRequest.tsx index 5412d1f2..4f0240f3 100644 --- a/src-web/hooks/useDeleteRequest.tsx +++ b/src-web/hooks/useDeleteRequest.tsx @@ -1,9 +1,9 @@ import { useMutation } from '@tanstack/react-query'; import type { HttpRequest } from '../lib/models'; -import { useDeleteAnyRequest } from './useDeleteAnyRequest'; +import { useDeleteAnyHttpRequest } from './useDeleteAnyHttpRequest'; export function useDeleteRequest(id: string | null) { - const deleteAnyRequest = useDeleteAnyRequest(); + const deleteAnyRequest = useDeleteAnyHttpRequest(); return useMutation({ mutationFn: () => deleteAnyRequest.mutateAsync(id ?? 'n/a'),