mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-23 18:01:08 +01:00
Template Tag Function Editor (#67)

This commit is contained in:
@@ -14,7 +14,7 @@ export function useActiveEnvironment() {
|
||||
|
||||
export const QUERY_ENVIRONMENT_ID = 'environment_id';
|
||||
|
||||
export function useActiveEnvironmentId() {
|
||||
function useActiveEnvironmentId() {
|
||||
// NOTE: This query param is accessed from Rust side, so do not change
|
||||
const [params, setParams] = useSearchParams();
|
||||
const id = params.get(QUERY_ENVIRONMENT_ID);
|
||||
|
||||
24
src-web/hooks/useActiveEnvironmentVariables.ts
Normal file
24
src-web/hooks/useActiveEnvironmentVariables.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { EnvironmentVariable } from '@yaakapp/api';
|
||||
import { useMemo } from 'react';
|
||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
|
||||
export function useActiveEnvironmentVariables() {
|
||||
const workspace = useActiveWorkspace();
|
||||
const [environment] = useActiveEnvironment();
|
||||
|
||||
const variables = useMemo(() => {
|
||||
const varMap: Record<string, EnvironmentVariable> = {};
|
||||
|
||||
const allVariables = [...(workspace?.variables ?? []), ...(environment?.variables ?? [])];
|
||||
|
||||
for (const v of allVariables) {
|
||||
if (!v.enabled || !v.name) continue;
|
||||
varMap[v.name] = v;
|
||||
}
|
||||
|
||||
return Object.values(varMap);
|
||||
}, [workspace, environment]);
|
||||
|
||||
return variables;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { Workspace } from '@yaakapp/api';
|
||||
import { useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import type { RouteParamsWorkspace } from './useAppRoutes';
|
||||
import { useWorkspaces } from './useWorkspaces';
|
||||
@@ -7,6 +7,7 @@ import { useWorkspaces } from './useWorkspaces';
|
||||
export function useActiveWorkspace(): Workspace | null {
|
||||
const workspaceId = useActiveWorkspaceId();
|
||||
const workspaces = useWorkspaces();
|
||||
|
||||
return useMemo(
|
||||
() => workspaces.find((w) => w.id === workspaceId) ?? null,
|
||||
[workspaces, workspaceId],
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
export interface AppInfo {
|
||||
@@ -9,12 +8,8 @@ export interface AppInfo {
|
||||
appLogDir: string;
|
||||
}
|
||||
|
||||
const appInfo = (await invokeCmd('cmd_metadata')) as AppInfo;
|
||||
|
||||
export function useAppInfo() {
|
||||
return useQuery({
|
||||
queryKey: ['appInfo'],
|
||||
queryFn: async () => {
|
||||
const metadata = await invokeCmd('cmd_metadata');
|
||||
return metadata as AppInfo;
|
||||
},
|
||||
}).data;
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export function useCheckForUpdates() {
|
||||
title: 'No Update Available',
|
||||
body: (
|
||||
<>
|
||||
You are currently on the latest version <InlineCode>{appInfo?.version}</InlineCode>
|
||||
You are currently on the latest version <InlineCode>{appInfo.version}</InlineCode>
|
||||
</>
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import type { GrpcRequest } from '@yaakapp/api';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
import { setKeyValue } from '../lib/keyValueStore';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useAppRoutes } from './useAppRoutes';
|
||||
import { protoFilesArgs, useGrpcProtoFiles } from './useGrpcProtoFiles';
|
||||
import { getGrpcProtoFiles, setGrpcProtoFiles } from './useGrpcProtoFiles';
|
||||
|
||||
export function useDuplicateGrpcRequest({
|
||||
id,
|
||||
@@ -18,7 +17,7 @@ export function useDuplicateGrpcRequest({
|
||||
const activeWorkspace = useActiveWorkspace();
|
||||
const [activeEnvironment] = useActiveEnvironment();
|
||||
const routes = useAppRoutes();
|
||||
const protoFiles = useGrpcProtoFiles(id);
|
||||
|
||||
return useMutation<GrpcRequest, string>({
|
||||
mutationKey: ['duplicate_grpc_request', id],
|
||||
mutationFn: async () => {
|
||||
@@ -27,8 +26,11 @@ export function useDuplicateGrpcRequest({
|
||||
},
|
||||
onSettled: () => trackEvent('grpc_request', 'duplicate'),
|
||||
onSuccess: async (request) => {
|
||||
if (id == null) return;
|
||||
|
||||
// Also copy proto files to new request
|
||||
await setKeyValue({ ...protoFilesArgs(request.id), value: protoFiles.value ?? [] });
|
||||
const protoFiles = await getGrpcProtoFiles(id);
|
||||
await setGrpcProtoFiles(request.id, protoFiles);
|
||||
|
||||
if (navigateAfter && activeWorkspace !== null) {
|
||||
routes.navigate('request', {
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { Environment } from '@yaakapp/api';
|
||||
import { atom, useAtom } from 'jotai/index';
|
||||
import { useEffect } from 'react';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
|
||||
export function environmentsQueryKey({ workspaceId }: { workspaceId: string }) {
|
||||
return ['environments', { workspaceId }];
|
||||
}
|
||||
export const environmentsAtom = atom<Environment[]>([]);
|
||||
|
||||
export function useEnvironments() {
|
||||
const [items, setItems] = useAtom(environmentsAtom);
|
||||
const workspace = useActiveWorkspace();
|
||||
return (
|
||||
useQuery({
|
||||
enabled: workspace != null,
|
||||
queryKey: environmentsQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
if (workspace == null) return [];
|
||||
return (await invokeCmd('cmd_list_environments', {
|
||||
workspaceId: workspace.id,
|
||||
})) as Environment[];
|
||||
},
|
||||
}).data ?? []
|
||||
);
|
||||
|
||||
// Fetch new requests when workspace changes
|
||||
useEffect(() => {
|
||||
if (workspace == null) return;
|
||||
invokeCmd<Environment[]>('cmd_list_environments', { workspaceId: workspace.id }).then(setItems);
|
||||
}, [setItems, workspace]);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export function useGrpcConnections(requestId: string | null) {
|
||||
initialData: [],
|
||||
queryKey: grpcConnectionsQueryKey({ requestId: requestId ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
if (requestId == null) return [];
|
||||
return (await invokeCmd('cmd_list_grpc_connections', {
|
||||
requestId,
|
||||
limit: 200,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function protoFilesArgs(requestId: string | null) {
|
||||
@@ -10,3 +11,11 @@ export function protoFilesArgs(requestId: string | null) {
|
||||
export function useGrpcProtoFiles(activeRequestId: string | null) {
|
||||
return useKeyValue<string[]>({ ...protoFilesArgs(activeRequestId), fallback: [] });
|
||||
}
|
||||
|
||||
export async function getGrpcProtoFiles(requestId: string) {
|
||||
return getKeyValue<string[]>({ ...protoFilesArgs(requestId), fallback: [] });
|
||||
}
|
||||
|
||||
export async function setGrpcProtoFiles(requestId: string, protoFiles: string[]) {
|
||||
return setKeyValue<string[]>({ ...protoFilesArgs(requestId), value: protoFiles });
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { GrpcRequest } from '@yaakapp/api';
|
||||
import { atom, useAtom } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
|
||||
export function grpcRequestsQueryKey({ workspaceId }: { workspaceId: string }) {
|
||||
return ['grpc_requests', { workspaceId }];
|
||||
}
|
||||
export const grpcRequestsAtom = atom<GrpcRequest[]>([]);
|
||||
|
||||
export function useGrpcRequests() {
|
||||
const [items, setItems] = useAtom(grpcRequestsAtom);
|
||||
const workspace = useActiveWorkspace();
|
||||
return (
|
||||
useQuery({
|
||||
enabled: workspace != null,
|
||||
queryKey: grpcRequestsQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
if (workspace == null) return [];
|
||||
return (await invokeCmd('cmd_list_grpc_requests', {
|
||||
workspaceId: workspace.id,
|
||||
})) as GrpcRequest[];
|
||||
},
|
||||
}).data ?? []
|
||||
);
|
||||
|
||||
// Fetch new requests when workspace changes
|
||||
useEffect(() => {
|
||||
if (workspace == null) return;
|
||||
invokeCmd<GrpcRequest[]>('cmd_list_grpc_requests', { workspaceId: workspace.id }).then(
|
||||
setItems,
|
||||
);
|
||||
}, [setItems, workspace]);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { invokeCmd } from '../lib/tauri';
|
||||
export function useHttpRequestActions() {
|
||||
const httpRequestActions = useQuery({
|
||||
queryKey: ['http_request_actions'],
|
||||
refetchOnWindowFocus: false,
|
||||
queryFn: async () => {
|
||||
const responses = (await invokeCmd(
|
||||
'cmd_http_request_actions',
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { HttpRequest } from '@yaakapp/api';
|
||||
import { atom, useAtom } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
|
||||
export function httpRequestsQueryKey({ workspaceId }: { workspaceId: string }) {
|
||||
return ['http_requests', { workspaceId }];
|
||||
}
|
||||
export const httpRequestsAtom = atom<HttpRequest[]>([]);
|
||||
|
||||
export function useHttpRequests() {
|
||||
const [items, setItems] = useAtom(httpRequestsAtom);
|
||||
const workspace = useActiveWorkspace();
|
||||
return (
|
||||
useQuery({
|
||||
enabled: workspace != null,
|
||||
queryKey: httpRequestsQueryKey({ workspaceId: workspace?.id ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
if (workspace == null) return [];
|
||||
return (await invokeCmd('cmd_list_http_requests', {
|
||||
workspaceId: workspace.id,
|
||||
})) as HttpRequest[];
|
||||
},
|
||||
}).data ?? []
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (workspace == null) return;
|
||||
invokeCmd<HttpRequest[]>('cmd_list_http_requests', { workspaceId: workspace.id }).then(
|
||||
setItems,
|
||||
);
|
||||
}, [setItems, workspace]);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export function useHttpResponses(requestId: string | null) {
|
||||
initialData: [],
|
||||
queryKey: httpResponsesQueryKey({ requestId: requestId ?? 'n/a' }),
|
||||
queryFn: async () => {
|
||||
if (requestId == null) return [];
|
||||
return (await invokeCmd('cmd_list_http_responses', {
|
||||
requestId,
|
||||
limit: 200,
|
||||
|
||||
14
src-web/hooks/useParseTemplate.ts
Normal file
14
src-web/hooks/useParseTemplate.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { Tokens } from '../gen/Tokens';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
export function useParseTemplate(template: string) {
|
||||
return useQuery<Tokens>({
|
||||
queryKey: ['parse_template', template],
|
||||
queryFn: () => parseTemplate(template),
|
||||
});
|
||||
}
|
||||
|
||||
export async function parseTemplate(template: string): Promise<Tokens> {
|
||||
return invokeCmd('cmd_parse_template', { template });
|
||||
}
|
||||
26
src-web/hooks/useRenderTemplate.ts
Normal file
26
src-web/hooks/useRenderTemplate.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
|
||||
export function useRenderTemplate(template: string) {
|
||||
const workspaceId = useActiveWorkspace()?.id ?? 'n/a';
|
||||
const environmentId = useActiveEnvironment()[0]?.id ?? null;
|
||||
return useQuery<string>({
|
||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||
queryKey: ['render_template', template],
|
||||
queryFn: () => renderTemplate({ template, workspaceId, environmentId }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function renderTemplate({
|
||||
template,
|
||||
workspaceId,
|
||||
environmentId,
|
||||
}: {
|
||||
template: string;
|
||||
workspaceId: string;
|
||||
environmentId: string | null;
|
||||
}): Promise<string> {
|
||||
return invokeCmd('cmd_render_template', { template, workspaceId, environmentId });
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export function useSyncWorkspaceRequestTitle() {
|
||||
newTitle += ` › ${fallbackRequestName(activeRequest)}`;
|
||||
}
|
||||
|
||||
if (appInfo?.isDev) {
|
||||
if (appInfo.isDev) {
|
||||
newTitle = `[DEV] ${newTitle}`;
|
||||
}
|
||||
|
||||
@@ -40,5 +40,5 @@ export function useSyncWorkspaceRequestTitle() {
|
||||
} else {
|
||||
emit('yaak_title_changed', newTitle).catch(console.error);
|
||||
}
|
||||
}, [activeEnvironment, activeRequest, activeWorkspace, appInfo?.isDev, osInfo.osType]);
|
||||
}, [activeEnvironment, activeRequest, activeWorkspace, appInfo.isDev, osInfo.osType]);
|
||||
}
|
||||
|
||||
88
src-web/hooks/useTemplateFunctions.ts
Normal file
88
src-web/hooks/useTemplateFunctions.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { HttpRequest } from '@yaakapp/api';
|
||||
|
||||
export interface TemplateFunctionArgBase {
|
||||
name: string;
|
||||
optional?: boolean;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface TemplateFunctionSelectArg extends TemplateFunctionArgBase {
|
||||
type: 'select';
|
||||
defaultValue?: string;
|
||||
options: readonly { name: string; value: string }[];
|
||||
}
|
||||
|
||||
export interface TemplateFunctionTextArg extends TemplateFunctionArgBase {
|
||||
type: 'text';
|
||||
defaultValue?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export interface TemplateFunctionHttpRequestArg extends TemplateFunctionArgBase {
|
||||
type: HttpRequest['model'];
|
||||
}
|
||||
|
||||
export type TemplateFunctionArg =
|
||||
| TemplateFunctionSelectArg
|
||||
| TemplateFunctionTextArg
|
||||
| TemplateFunctionHttpRequestArg;
|
||||
|
||||
export interface TemplateFunction {
|
||||
name: string;
|
||||
args: TemplateFunctionArg[];
|
||||
}
|
||||
|
||||
export function useTemplateFunctions() {
|
||||
const fns: TemplateFunction[] = [
|
||||
{
|
||||
name: 'timestamp',
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'from',
|
||||
label: 'From',
|
||||
placeholder: '2023-23-12T04:03:03',
|
||||
optional: true,
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: 'Format',
|
||||
name: 'format',
|
||||
options: [
|
||||
{ name: 'RFC3339', value: 'rfc3339' },
|
||||
{ name: 'Unix', value: 'unix' },
|
||||
{ name: 'Unix (ms)', value: 'unix_millis' },
|
||||
],
|
||||
optional: true,
|
||||
defaultValue: 'RFC3339',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'response',
|
||||
args: [
|
||||
{
|
||||
type: 'http_request',
|
||||
name: 'request',
|
||||
label: 'Request',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'attribute',
|
||||
label: 'Attribute',
|
||||
options: [
|
||||
{ name: 'Body', value: 'body' },
|
||||
{ name: 'Header', value: 'header' },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'filter',
|
||||
label: 'Filter',
|
||||
placeholder: 'JSONPath or XPath expression',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
return fns;
|
||||
}
|
||||
15
src-web/hooks/useTemplateTokensToString.ts
Normal file
15
src-web/hooks/useTemplateTokensToString.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { Tokens } from '../gen/Tokens';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
export function useTemplateTokensToString(tokens: Tokens) {
|
||||
return useQuery<string>({
|
||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||
queryKey: ['template_tokens_to_string', tokens],
|
||||
queryFn: () => templateTokensToString(tokens),
|
||||
});
|
||||
}
|
||||
|
||||
export async function templateTokensToString(tokens: Tokens): Promise<string> {
|
||||
return invokeCmd('cmd_template_tokens_to_string', { tokens });
|
||||
}
|
||||
@@ -1,20 +1,10 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { Workspace } from '@yaakapp/api';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { listWorkspaces } from '../lib/store';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/ban-types
|
||||
export function workspacesQueryKey(_?: {}) {
|
||||
return ['workspaces'];
|
||||
}
|
||||
const workspaces = await listWorkspaces();
|
||||
export const workspacesAtom = atom<Workspace[]>(workspaces);
|
||||
|
||||
export function useWorkspaces() {
|
||||
return (
|
||||
useQuery({
|
||||
queryKey: workspacesQueryKey(),
|
||||
queryFn: async () => {
|
||||
const workspaces = await invokeCmd('cmd_list_workspaces');
|
||||
return workspaces as Workspace[];
|
||||
},
|
||||
}).data ?? []
|
||||
);
|
||||
return useAtomValue(workspacesAtom);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user