mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-05 08:47:05 +02:00
Merge pull request #256
* Update environment model to get ready for request/folder environments * Folder environments in UI * Folder environments working * Tweaks and fixes * Tweak environment encryption UX * Tweak environment encryption UX * Address comments * Update fn name * Add tsc back to lint rules * Update src-web/components/EnvironmentEditor.tsx * Merge remote-tracking branch 'origin/folder-environments' into folder…
This commit is contained in:
@@ -4,5 +4,5 @@ import { useEnvironmentVariables } from './useEnvironmentVariables';
|
||||
|
||||
export function useActiveEnvironmentVariables() {
|
||||
const activeEnvironment = useAtomValue(activeEnvironmentAtom);
|
||||
return useEnvironmentVariables(activeEnvironment?.id ?? null);
|
||||
return useEnvironmentVariables(activeEnvironment?.id ?? null).map((v) => v.variable);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export function useCopyHttpResponse(response: HttpResponse) {
|
||||
return useFastMutation({
|
||||
mutationKey: ['copy_http_response', response.id],
|
||||
async mutationFn() {
|
||||
const body = await getResponseBodyText(response);
|
||||
const body = await getResponseBodyText({ responseId: response.id, filter: null });
|
||||
copyToClipboard(body);
|
||||
},
|
||||
});
|
||||
|
||||
10
src-web/hooks/useEnvironmentValueVisibility.ts
Normal file
10
src-web/hooks/useEnvironmentValueVisibility.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function useEnvironmentValueVisibility(environment: Environment) {
|
||||
return useKeyValue<boolean>({
|
||||
namespace: 'global',
|
||||
key: ['environmentValueVisibility', environment.workspaceId],
|
||||
fallback: false,
|
||||
});
|
||||
}
|
||||
@@ -1,25 +1,52 @@
|
||||
import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import { environmentsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type { Environment, EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import { foldersAtom } from '@yaakapp-internal/models';
|
||||
import { useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { useActiveRequest } from './useActiveRequest';
|
||||
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
|
||||
import { useParentFolders } from './useParentFolders';
|
||||
|
||||
export function useEnvironmentVariables(environmentId: string | null) {
|
||||
const { baseEnvironment } = useEnvironmentsBreakdown();
|
||||
const activeEnvironment =
|
||||
useAtomValue(environmentsAtom).find((e) => e.id === environmentId) ?? null;
|
||||
const { baseEnvironment, folderEnvironments, subEnvironments } = useEnvironmentsBreakdown();
|
||||
const activeEnvironment = subEnvironments.find((e) => e.id === environmentId) ?? null;
|
||||
const activeRequest = useActiveRequest();
|
||||
const parentFolders = useParentFolders(activeRequest);
|
||||
|
||||
return useMemo(() => {
|
||||
const varMap: Record<string, EnvironmentVariable> = {};
|
||||
const varMap: Record<string, WrappedEnvironmentVariable> = {};
|
||||
const folderVariables = parentFolders.flatMap((f) =>
|
||||
wrapVariables(folderEnvironments.find((fe) => fe.parentId === f.id) ?? null),
|
||||
);
|
||||
|
||||
const allVariables = [
|
||||
...(baseEnvironment?.variables ?? []),
|
||||
...(activeEnvironment?.variables ?? []),
|
||||
...folderVariables,
|
||||
...wrapVariables(activeEnvironment),
|
||||
...wrapVariables(baseEnvironment),
|
||||
];
|
||||
|
||||
for (const v of allVariables) {
|
||||
if (!v.enabled || !v.name) continue;
|
||||
varMap[v.name] = v;
|
||||
if (!v.variable.enabled || !v.variable.name || v.variable.name in varMap) {
|
||||
continue;
|
||||
}
|
||||
varMap[v.variable.name] = v;
|
||||
}
|
||||
|
||||
return Object.values(varMap);
|
||||
}, [activeEnvironment, baseEnvironment]);
|
||||
}, [activeEnvironment, baseEnvironment, folderEnvironments, parentFolders]);
|
||||
}
|
||||
|
||||
export interface WrappedEnvironmentVariable {
|
||||
variable: EnvironmentVariable;
|
||||
environment: Environment;
|
||||
source: string;
|
||||
}
|
||||
|
||||
function wrapVariables(e: Environment | null): WrappedEnvironmentVariable[] {
|
||||
if (e == null) return [];
|
||||
const folders = jotaiStore.get(foldersAtom);
|
||||
return e.variables.map((v) => {
|
||||
const folder = e.parentModel === 'folder' ? folders.find((f) => f.id === e.parentId) : null;
|
||||
const source = folder?.name ?? e.name;
|
||||
return { variable: v, environment: e, source };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ import { useMemo } from 'react';
|
||||
export function useEnvironmentsBreakdown() {
|
||||
const allEnvironments = useAtomValue(environmentsAtom);
|
||||
return useMemo(() => {
|
||||
const baseEnvironments = allEnvironments.filter((e) => e.base) ?? [];
|
||||
const subEnvironments = allEnvironments.filter((e) => !e.base) ?? [];
|
||||
const baseEnvironments = allEnvironments.filter((e) => e.parentId == null) ?? [];
|
||||
const subEnvironments =
|
||||
allEnvironments.filter((e) => e.parentModel === 'environment' && e.parentId != null) ?? [];
|
||||
const folderEnvironments =
|
||||
allEnvironments.filter((e) => e.parentModel === 'folder' && e.parentId != null) ?? [];
|
||||
|
||||
const baseEnvironment = baseEnvironments[0] ?? null;
|
||||
const otherBaseEnvironments =
|
||||
baseEnvironments.filter((e) => e.id !== baseEnvironment?.id) ?? [];
|
||||
return { allEnvironments, baseEnvironment, subEnvironments, otherBaseEnvironments };
|
||||
return { allEnvironments, baseEnvironment, subEnvironments, folderEnvironments, otherBaseEnvironments };
|
||||
}, [allEnvironments]);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ const hotkeys: Record<HotkeyAction, string[]> = {
|
||||
'request_switcher.prev': ['Control+Tab'],
|
||||
'request_switcher.toggle': ['CmdCtrl+p'],
|
||||
'settings.show': ['CmdCtrl+,'],
|
||||
'sidebar.delete_selected_item': ['Backspace'],
|
||||
'sidebar.delete_selected_item': ['Delete'],
|
||||
'sidebar.focus': ['CmdCtrl+b'],
|
||||
'url_bar.focus': ['CmdCtrl+l'],
|
||||
'workspace_settings.show': ['CmdCtrl+;'],
|
||||
@@ -98,7 +98,7 @@ export function useHotKey(
|
||||
|
||||
// Don't add key if not holding modifier
|
||||
const isValidKeymapKey =
|
||||
e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.key === 'Backspace';
|
||||
e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.key === 'Backspace' || e.key === 'Delete';
|
||||
if (!isValidKeymapKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
export function useHttpAuthenticationConfig(
|
||||
authName: string | null,
|
||||
values: Record<string, JsonPrimitive>,
|
||||
requestId: string,
|
||||
request: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||
) {
|
||||
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const environmentId = useAtomValue(activeEnvironmentIdAtom);
|
||||
@@ -37,7 +37,7 @@ export function useHttpAuthenticationConfig(
|
||||
return useQuery({
|
||||
queryKey: [
|
||||
'http_authentication_config',
|
||||
requestId,
|
||||
request,
|
||||
authName,
|
||||
values,
|
||||
responseKey,
|
||||
@@ -53,8 +53,7 @@ export function useHttpAuthenticationConfig(
|
||||
{
|
||||
authName,
|
||||
values,
|
||||
requestId,
|
||||
workspaceId,
|
||||
request,
|
||||
environmentId,
|
||||
},
|
||||
);
|
||||
@@ -63,17 +62,16 @@ export function useHttpAuthenticationConfig(
|
||||
...config,
|
||||
actions: config.actions?.map((a, i) => ({
|
||||
...a,
|
||||
call: async ({
|
||||
id: modelId,
|
||||
}: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace) => {
|
||||
call: async (
|
||||
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||
) => {
|
||||
await invokeCmd('cmd_call_http_authentication_action', {
|
||||
pluginRefId: config.pluginRefId,
|
||||
actionIndex: i,
|
||||
authName,
|
||||
values,
|
||||
modelId,
|
||||
model,
|
||||
environmentId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
// Ensure the config is refreshed after the action is done
|
||||
|
||||
@@ -66,7 +66,7 @@ export function useIntrospectGraphQL(
|
||||
return setError(response.error);
|
||||
}
|
||||
|
||||
const bodyText = await getResponseBodyText(response);
|
||||
const bodyText = await getResponseBodyText({ responseId: response.id, filter: null });
|
||||
if (response.status < 200 || response.status >= 300) {
|
||||
return setError(
|
||||
`Request failed with status ${response.status}.\nThe response text is:\n\n${bodyText}`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useAtomValue} from "jotai/index";
|
||||
import {activeWorkspaceMetaAtom} from "./useActiveWorkspace";
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { activeWorkspaceMetaAtom } from './useActiveWorkspace';
|
||||
|
||||
export function useIsEncryptionEnabled() {
|
||||
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
|
||||
return workspaceMeta?.encryptionKey != null;
|
||||
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
|
||||
return workspaceMeta?.encryptionKey != null;
|
||||
}
|
||||
|
||||
24
src-web/hooks/useParentFolders.ts
Normal file
24
src-web/hooks/useParentFolders.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import { foldersAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export function useParentFolders(m: Folder | HttpRequest | GrpcRequest | WebsocketRequest | null) {
|
||||
const folders = useAtomValue(foldersAtom);
|
||||
|
||||
return useMemo(() => getParentFolders(folders, m), [folders, m]);
|
||||
}
|
||||
|
||||
function getParentFolders(
|
||||
folders: Folder[],
|
||||
currentModel: Folder | HttpRequest | GrpcRequest | WebsocketRequest | null,
|
||||
): Folder[] {
|
||||
if (currentModel == null) return [];
|
||||
|
||||
const folder = currentModel.folderId ? folders.find((f) => f.id === currentModel.folderId) : null;
|
||||
if (folder == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [folder, ...getParentFolders(folders, folder)];
|
||||
}
|
||||
@@ -1,15 +1,22 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { getResponseBodyText } from '../lib/responseBody';
|
||||
|
||||
export function useResponseBodyText({
|
||||
responseId,
|
||||
response,
|
||||
filter,
|
||||
}: {
|
||||
responseId: string;
|
||||
response: HttpResponse;
|
||||
filter: string | null;
|
||||
}) {
|
||||
return useQuery({
|
||||
queryKey: ['response_body_text', responseId, filter ?? ''],
|
||||
queryFn: () => getResponseBodyText({ responseId, filter }),
|
||||
queryKey: [
|
||||
'response_body_text',
|
||||
response.id,
|
||||
response.updatedAt,
|
||||
response.contentLength,
|
||||
filter ?? '',
|
||||
],
|
||||
queryFn: () => getResponseBodyText({ responseId: response.id, filter }),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user