Show gRPC requests in sidebar

This commit is contained in:
Gregory Schier
2024-02-03 13:08:24 -08:00
parent 04f31cd4a7
commit 23431b40e9
33 changed files with 389 additions and 149 deletions

View File

@@ -1,9 +1,28 @@
import type { HttpRequest } from '../lib/models';
import { r } from 'vitest/dist/types-94cfe4b4';
import type { GrpcRequest, HttpRequest } from '../lib/models';
import { useActiveRequestId } from './useActiveRequestId';
import { useRequests } from './useRequests';
import { useGrpcRequests } from './useGrpcRequests';
import { useHttpRequests } from './useHttpRequests';
export function useActiveRequest(): HttpRequest | null {
const requestId = useActiveRequestId();
const requests = useRequests();
return requests.find((r) => r.id === requestId) ?? null;
interface TypeMap {
http_request: HttpRequest;
grpc_request: GrpcRequest;
}
export function useActiveRequest<T extends keyof TypeMap>(
model?: T | undefined,
): TypeMap[T] | null {
const requestId = useActiveRequestId();
const httpRequests = useHttpRequests();
const grpcRequests = useGrpcRequests();
if (model === 'http_request') {
return (httpRequests.find((r) => r.id === requestId) ?? null) as TypeMap[T] | null;
} else if (model === 'grpc_request') {
return (grpcRequests.find((r) => r.id === requestId) ?? null) as TypeMap[T] | null;
} else {
return (grpcRequests.find((r) => r.id === requestId) ??
httpRequests.find((r) => r.id === requestId) ??
null) as TypeMap[T] | null;
}
}

View File

@@ -0,0 +1,55 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api';
import { trackEvent } from '../lib/analytics';
import type { GrpcRequest, HttpRequest } from '../lib/models';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveRequest } from './useActiveRequest';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useAppRoutes } from './useAppRoutes';
import { grpcRequestsQueryKey } from './useGrpcRequests';
import { httpRequestsQueryKey } from './useHttpRequests';
export function useCreateGrpcRequest() {
const workspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
// const activeRequest = useActiveRequest();
const activeRequest = null;
const routes = useAppRoutes();
const queryClient = useQueryClient();
return useMutation<
GrpcRequest,
unknown,
Partial<Pick<GrpcRequest, 'name' | 'sortPriority' | 'folderId'>>
>({
mutationFn: (patch) => {
if (workspaceId === null) {
throw new Error("Cannot create grpc request when there's no active workspace");
}
if (patch.sortPriority === undefined) {
if (activeRequest != null) {
// Place above currently-active request
// patch.sortPriority = activeRequest.sortPriority + 0.0001;
} else {
// Place at the very top
patch.sortPriority = -Date.now();
}
}
// patch.folderId = patch.folderId; // TODO: || activeRequest?.folderId;
return invoke('cmd_create_grpc_request', { workspaceId, name: '', ...patch });
},
onSettled: () => trackEvent('GrpcRequest', 'Create'),
onSuccess: async (request) => {
queryClient.setQueryData<GrpcRequest[]>(
grpcRequestsQueryKey({ workspaceId: request.workspaceId }),
(requests) => [...(requests ?? []), request],
);
// TODO: This should navigate to the new request
routes.navigate('request', {
workspaceId: request.workspaceId,
requestId: request.id,
environmentId: activeEnvironmentId ?? undefined,
});
},
});
}

View File

@@ -6,9 +6,9 @@ import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveRequest } from './useActiveRequest';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useAppRoutes } from './useAppRoutes';
import { requestsQueryKey } from './useRequests';
import { httpRequestsQueryKey } from './useHttpRequests';
export function useCreateRequest() {
export function useCreateHttpRequest() {
const workspaceId = useActiveWorkspaceId();
const activeEnvironmentId = useActiveEnvironmentId();
const activeRequest = useActiveRequest();
@@ -34,12 +34,12 @@ export function useCreateRequest() {
}
}
patch.folderId = patch.folderId || activeRequest?.folderId;
return invoke('cmd_create_request', { workspaceId, name: '', ...patch });
return invoke('cmd_create_http_request', { workspaceId, name: '', ...patch });
},
onSettled: () => trackEvent('HttpRequest', 'Create'),
onSuccess: async (request) => {
queryClient.setQueryData<HttpRequest[]>(
requestsQueryKey({ workspaceId: request.workspaceId }),
httpRequestsQueryKey({ workspaceId: request.workspaceId }),
(requests) => [...(requests ?? []), request],
);
routes.navigate('request', {

View File

@@ -6,7 +6,7 @@ import { fallbackRequestName } from '../lib/fallbackRequestName';
import type { HttpRequest } from '../lib/models';
import { getRequest } from '../lib/store';
import { useConfirm } from './useConfirm';
import { requestsQueryKey } from './useRequests';
import { httpRequestsQueryKey } from './useHttpRequests';
import { responsesQueryKey } from './useResponses';
export function useDeleteAnyRequest() {
@@ -27,7 +27,7 @@ export function useDeleteAnyRequest() {
),
});
if (!confirmed) return null;
return invoke('cmd_delete_request', { requestId: id });
return invoke('cmd_delete_http_request', { requestId: id });
},
onSettled: () => trackEvent('HttpRequest', 'Delete'),
onSuccess: async (request) => {
@@ -36,7 +36,7 @@ export function useDeleteAnyRequest() {
const { workspaceId, id: requestId } = request;
queryClient.setQueryData(responsesQueryKey({ requestId }), []); // Responses were deleted
queryClient.setQueryData<HttpRequest[]>(requestsQueryKey({ workspaceId }), (requests) =>
queryClient.setQueryData<HttpRequest[]>(httpRequestsQueryKey({ workspaceId }), (requests) =>
(requests ?? []).filter((r) => r.id !== requestId),
);
},

View File

@@ -6,7 +6,7 @@ import type { Folder } from '../lib/models';
import { getFolder } from '../lib/store';
import { useConfirm } from './useConfirm';
import { foldersQueryKey } from './useFolders';
import { requestsQueryKey } from './useRequests';
import { httpRequestsQueryKey } from './useHttpRequests';
export function useDeleteFolder(id: string | null) {
const queryClient = useQueryClient();
@@ -36,7 +36,7 @@ export function useDeleteFolder(id: string | null) {
const { workspaceId } = folder;
// Nesting makes it hard to clean things up, so just clear everything that could have been deleted
await queryClient.invalidateQueries(requestsQueryKey({ workspaceId }));
await queryClient.invalidateQueries(httpRequestsQueryKey({ workspaceId }));
await queryClient.invalidateQueries(foldersQueryKey({ workspaceId }));
},
});

View File

@@ -6,7 +6,7 @@ import type { Workspace } from '../lib/models';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useAppRoutes } from './useAppRoutes';
import { useConfirm } from './useConfirm';
import { requestsQueryKey } from './useRequests';
import { httpRequestsQueryKey } from './useHttpRequests';
import { workspacesQueryKey } from './useWorkspaces';
export function useDeleteWorkspace(workspace: Workspace | null) {
@@ -43,8 +43,8 @@ export function useDeleteWorkspace(workspace: Workspace | null) {
}
// Also clean up other things that may have been deleted
queryClient.setQueryData(requestsQueryKey({ workspaceId }), []);
await queryClient.invalidateQueries(requestsQueryKey({ workspaceId }));
queryClient.setQueryData(httpRequestsQueryKey({ workspaceId }), []);
await queryClient.invalidateQueries(httpRequestsQueryKey({ workspaceId }));
},
});
}

View File

@@ -5,7 +5,7 @@ import type { HttpRequest } from '../lib/models';
import { useActiveEnvironmentId } from './useActiveEnvironmentId';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
import { useAppRoutes } from './useAppRoutes';
import { requestsQueryKey } from './useRequests';
import { httpRequestsQueryKey } from './useHttpRequests';
export function useDuplicateRequest({
id,
@@ -21,12 +21,12 @@ export function useDuplicateRequest({
return useMutation<HttpRequest, string>({
mutationFn: async () => {
if (id === null) throw new Error("Can't duplicate a null request");
return invoke('cmd_duplicate_request', { id });
return invoke('cmd_duplicate_http_request', { id });
},
onSettled: () => trackEvent('HttpRequest', 'Duplicate'),
onSuccess: async (request) => {
queryClient.setQueryData<HttpRequest[]>(
requestsQueryKey({ workspaceId: request.workspaceId }),
httpRequestsQueryKey({ workspaceId: request.workspaceId }),
(requests) => [...(requests ?? []), request],
);
if (navigateAfter && activeWorkspaceId !== null) {

View File

@@ -0,0 +1,22 @@
import { useQuery } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api';
import type { GrpcRequest } from '../lib/models';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
export function grpcRequestsQueryKey({ workspaceId }: { workspaceId: string }) {
return ['grpc_requests', { workspaceId }];
}
export function useGrpcRequests() {
const workspaceId = useActiveWorkspaceId();
return (
useQuery({
enabled: workspaceId != null,
queryKey: grpcRequestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
queryFn: async () => {
if (workspaceId == null) return [];
return (await invoke('cmd_list_grpc_requests', { workspaceId })) as GrpcRequest[];
},
}).data ?? []
);
}

View File

@@ -6,11 +6,11 @@ import { useOsInfo } from './useOsInfo';
export type HotkeyAction =
| 'environmentEditor.toggle'
| 'grpc.send'
| 'hotkeys.showHelp'
| 'request.create'
| 'request.duplicate'
| 'request.send'
| 'grpc_request.send'
| 'http_request.create'
| 'http_request.duplicate'
| 'http_request.send'
| 'requestSwitcher.next'
| 'requestSwitcher.prev'
| 'settings.show'
@@ -20,11 +20,11 @@ export type HotkeyAction =
const hotkeys: Record<HotkeyAction, string[]> = {
'environmentEditor.toggle': ['CmdCtrl+Shift+e'],
'grpc.send': ['CmdCtrl+Enter', 'CmdCtrl+r'],
'grpc_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'],
'hotkeys.showHelp': ['CmdCtrl+Shift+/'],
'request.create': ['CmdCtrl+n'],
'request.duplicate': ['CmdCtrl+d'],
'request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'],
'http_request.create': ['CmdCtrl+n'],
'http_request.duplicate': ['CmdCtrl+d'],
'http_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'],
'requestSwitcher.next': ['Control+Shift+Tab'],
'requestSwitcher.prev': ['Control+Tab'],
'settings.show': ['CmdCtrl+,'],
@@ -35,11 +35,11 @@ const hotkeys: Record<HotkeyAction, string[]> = {
const hotkeyLabels: Record<HotkeyAction, string> = {
'environmentEditor.toggle': 'Edit Environments',
'grpc.send': 'Send Message',
'grpc_request.send': 'Send Message',
'hotkeys.showHelp': 'Show Keyboard Shortcuts',
'request.create': 'New Request',
'request.duplicate': 'Duplicate Request',
'request.send': 'Send Request',
'http_request.create': 'New Request',
'http_request.duplicate': 'Duplicate Request',
'http_request.send': 'Send Request',
'requestSwitcher.next': 'Go To Previous Request',
'requestSwitcher.prev': 'Go To Next Request',
'settings.show': 'Open Settings',

View File

@@ -3,19 +3,19 @@ import { invoke } from '@tauri-apps/api';
import type { HttpRequest } from '../lib/models';
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
export function requestsQueryKey({ workspaceId }: { workspaceId: string }) {
export function httpRequestsQueryKey({ workspaceId }: { workspaceId: string }) {
return ['http_requests', { workspaceId }];
}
export function useRequests() {
export function useHttpRequests() {
const workspaceId = useActiveWorkspaceId();
return (
useQuery({
enabled: workspaceId != null,
queryKey: requestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
queryKey: httpRequestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }),
queryFn: async () => {
if (workspaceId == null) return [];
return (await invoke('cmd_list_requests', { workspaceId })) as HttpRequest[];
return (await invoke('cmd_list_http_requests', { workspaceId })) as HttpRequest[];
},
}).data ?? []
);

View File

@@ -1,7 +1,7 @@
import type { HttpRequest } from '../lib/models';
import { useRequests } from './useRequests';
import { useHttpRequests } from './useHttpRequests';
export function useRequest(id: string | null): HttpRequest | null {
const requests = useRequests();
const requests = useHttpRequests();
return requests.find((r) => r.id === id) ?? null;
}

View File

@@ -2,9 +2,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api';
import type { HttpRequest } from '../lib/models';
import { getRequest } from '../lib/store';
import { requestsQueryKey } from './useRequests';
import { httpRequestsQueryKey } from './useHttpRequests';
export function useUpdateAnyRequest() {
export function useUpdateAnyHttpRequest() {
const queryClient = useQueryClient();
return useMutation<
@@ -20,14 +20,14 @@ export function useUpdateAnyRequest() {
const patchedRequest =
typeof update === 'function' ? update(request) : { ...request, ...update };
await invoke('cmd_update_request', { request: patchedRequest });
await invoke('cmd_update_http_request', { request: patchedRequest });
},
onMutate: async ({ id, update }) => {
const request = await getRequest(id);
if (request === null) return;
const patchedRequest =
typeof update === 'function' ? update(request) : { ...request, ...update };
queryClient.setQueryData<HttpRequest[]>(requestsQueryKey(request), (requests) =>
queryClient.setQueryData<HttpRequest[]>(httpRequestsQueryKey(request), (requests) =>
(requests ?? []).map((r) => (r.id === patchedRequest.id ? patchedRequest : r)),
);
},

View File

@@ -1,9 +1,9 @@
import { useMutation } from '@tanstack/react-query';
import type { HttpRequest } from '../lib/models';
import { useUpdateAnyRequest } from './useUpdateAnyRequest';
import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest';
export function useUpdateRequest(id: string | null) {
const updateAnyRequest = useUpdateAnyRequest();
export function useUpdateHttpRequest(id: string | null) {
const updateAnyRequest = useUpdateAnyHttpRequest();
return useMutation<void, unknown, Partial<HttpRequest> | ((r: HttpRequest) => HttpRequest)>({
mutationFn: async (update) => updateAnyRequest.mutateAsync({ id: id ?? 'n/a', update }),
});