Dynamic template function args and TTL option for request chaining (#266)

This commit is contained in:
Gregory Schier
2025-10-16 14:39:30 -07:00
committed by GitHub
parent d46479cd22
commit d83aabd2be
15 changed files with 365 additions and 95 deletions

View File

@@ -1,9 +1,17 @@
import type {
Folder,
GrpcRequest,
HttpRequest,
WebsocketRequest,
Workspace,
} from '@yaakapp-internal/models';
import type { TemplateFunction } from '@yaakapp-internal/plugins';
import type { FnArg, Tokens } from '@yaakapp-internal/templates';
import classNames from 'classnames';
import { useEffect, useMemo, useState } from 'react';
import { useDebouncedValue } from '../hooks/useDebouncedValue';
import { useRenderTemplate } from '../hooks/useRenderTemplate';
import { useTemplateFunctionConfig } from '../hooks/useTemplateFunctionConfig';
import {
templateTokensToString,
useTemplateTokensToString,
@@ -24,6 +32,7 @@ interface Props {
initialTokens: Tokens;
hide: () => void;
onChange: (insert: string) => void;
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace;
}
export function TemplateFunctionDialog({ initialTokens, templateFunction, ...props }: Props) {
@@ -84,14 +93,15 @@ export function TemplateFunctionDialog({ initialTokens, templateFunction, ...pro
}
function InitializedTemplateFunctionDialog({
templateFunction,
hide,
templateFunction: { name },
initialArgValues,
hide,
onChange,
model,
}: Omit<Props, 'initialTokens'> & {
initialArgValues: Record<string, string | boolean>;
}) {
const enablePreview = templateFunction.name !== 'secure';
const enablePreview = name !== 'secure';
const [showSecretsInPreview, toggleShowSecretsInPreview] = useToggle(false);
const [argValues, setArgValues] = useState<Record<string, string | boolean>>(initialArgValues);
@@ -112,15 +122,16 @@ function InitializedTemplateFunctionDialog({
type: 'tag',
val: {
type: 'fn',
name: templateFunction.name,
name,
args: argTokens,
},
},
],
};
}, [argValues, templateFunction.name]);
}, [argValues, name]);
const tagText = useTemplateTokensToString(tokens);
const templateFunction = useTemplateFunctionConfig(name, argValues, model).data;
const handleDone = () => {
if (tagText.data) {
@@ -134,7 +145,7 @@ function InitializedTemplateFunctionDialog({
const tooLarge = rendered.data ? rendered.data.length > 10000 : false;
const dataContainsSecrets = useMemo(() => {
for (const [name, value] of Object.entries(argValues)) {
const arg = templateFunction.args.find((a) => 'name' in a && a.name === name);
const arg = templateFunction?.args.find((a) => 'name' in a && a.name === name);
const isTextPassword = arg?.type === 'text' && arg.password;
if (isTextPassword && typeof value === 'string' && value && rendered.data?.includes(value)) {
return true;
@@ -145,6 +156,8 @@ function InitializedTemplateFunctionDialog({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rendered.data]);
if (templateFunction == null) return null;
return (
<VStack
as="form"
@@ -155,7 +168,7 @@ function InitializedTemplateFunctionDialog({
handleDone();
}}
>
{templateFunction.name === 'secure' ? (
{name === 'secure' ? (
<PlainInput
required
label="Value"

View File

@@ -28,6 +28,7 @@ import {
useMemo,
useRef,
} from 'react';
import { activeWorkspaceAtom } from '../../../hooks/useActiveWorkspace';
import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables';
import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables';
import { useRandomKey } from '../../../hooks/useRandomKey';
@@ -36,6 +37,7 @@ import { useTemplateFunctionCompletionOptions } from '../../../hooks/useTemplate
import { showDialog } from '../../../lib/dialog';
import { editEnvironment } from '../../../lib/editEnvironment';
import { tryFormatJson, tryFormatXml } from '../../../lib/formatters';
import { jotaiStore } from '../../../lib/jotai';
import { withEncryptionEnabled } from '../../../lib/setupOrConfigureEncryption';
import { TemplateFunctionDialog } from '../../TemplateFunctionDialog';
import { TemplateVariableDialog } from '../../TemplateVariableDialog';
@@ -292,18 +294,22 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
size: 'md',
title: <InlineCode>{fn.name}()</InlineCode>,
description: fn.description,
render: ({ hide }) => (
<TemplateFunctionDialog
templateFunction={fn}
hide={hide}
initialTokens={initialTokens}
onChange={(insert) => {
cm.current?.view.dispatch({
changes: [{ from: startPos, to: startPos + tagValue.length, insert }],
});
}}
/>
),
render: ({ hide }) => {
const model = jotaiStore.get(activeWorkspaceAtom)!;
return (
<TemplateFunctionDialog
templateFunction={fn}
model={model}
hide={hide}
initialTokens={initialTokens}
onChange={(insert) => {
cm.current?.view.dispatch({
changes: [{ from: startPos, to: startPos + tagValue.length, insert }],
});
}}
/>
);
},
});
if (fn.name === 'secure') {