diff --git a/web/app/components/workflow/nodes/http/components/edit-body/index.tsx b/web/app/components/workflow/nodes/http/components/edit-body/index.tsx index 05ff070c2c..8997254c92 100644 --- a/web/app/components/workflow/nodes/http/components/edit-body/index.tsx +++ b/web/app/components/workflow/nodes/http/components/edit-body/index.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useEffect } from 'react' +import React, { useCallback, useMemo } from 'react' import produce from 'immer' -import type { Body } from '../../types' -import { BodyType } from '../../types' -import useKeyValueList from '../../hooks/use-key-value-list' +import { uniqueId } from 'lodash-es' +import type { Body, BodyPayload, KeyValue as KeyValueType } from '../../types' +import { BodyPayloadValueType, BodyType } from '../../types' import KeyValue from '../key-value' import useAvailableVarList from '../../../_base/hooks/use-available-var-list' import VarReferencePicker from '../../../_base/components/variable/var-reference-picker' @@ -13,6 +13,8 @@ import InputWithVar from '@/app/components/workflow/nodes/_base/components/promp import type { ValueSelector, Var } from '@/app/components/workflow/types' import { VarType } from '@/app/components/workflow/types' +const UNIQUE_ID_PREFIX = 'key-value-' + type Props = { readonly: boolean nodeId: string @@ -43,7 +45,15 @@ const EditBody: FC = ({ payload, onChange, }) => { - const { type } = payload + const { type, data } = payload + const bodyPayload = useMemo(() => { + if (typeof data === 'string') { // old data + return [] + } + return data + }, [data]) + const stringValue = [BodyType.formData, BodyType.xWwwFormUrlencoded].includes(type) ? '' : (bodyPayload[0]?.value || '') + const { availableVars, availableNodes } = useAvailableVarList(nodeId, { onlyLeafNodeVar: false, filterVar: (varPayload: Var) => { @@ -55,51 +65,44 @@ const EditBody: FC = ({ const newType = e.target.value as BodyType onChange({ type: newType, - data: '', + data: [], }) - // eslint-disable-next-line @typescript-eslint/no-use-before-define - setBody([]) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [onChange]) - const isCurrentKeyValue = type === BodyType.formData || type === BodyType.xWwwFormUrlencoded - - const { - list: body, - setList: setBody, - addItem: addBody, - } = useKeyValueList(payload.data, (value) => { - if (!isCurrentKeyValue) - return - - const newBody = produce(payload, (draft: Body) => { - draft.data = value + const handleAddBody = useCallback(() => { + const newPayload = produce(payload, (draft) => { + (draft.data as BodyPayload).push({ + id: uniqueId(UNIQUE_ID_PREFIX), + type: BodyPayloadValueType.text, + key: '', + value: '', + }) }) - onChange(newBody) - }, type === BodyType.json) + onChange(newPayload) + }, [onChange, payload]) + + const handleBodyPayloadChange = useCallback((newList: KeyValueType[]) => { + const newPayload = produce(payload, (draft) => { + draft.data = newList as BodyPayload + }) + onChange(newPayload) + }, [onChange, payload]) const filterOnlyFileVariable = (varPayload: Var) => { return varPayload.type === VarType.file } - useEffect(() => { - if (!isCurrentKeyValue) - return - - const newBody = produce(payload, (draft: Body) => { - draft.data = body.map((item) => { - if (!item.key && !item.value) - return '' - return `${item.key}:${item.value}` - }).join('\n') - }) - onChange(newBody) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isCurrentKeyValue]) - const handleBodyValueChange = useCallback((value: string) => { const newBody = produce(payload, (draft: Body) => { - draft.data = value + if ((draft.data as BodyPayload).length === 0) { + (draft.data as BodyPayload).push({ + id: uniqueId(UNIQUE_ID_PREFIX), + type: BodyPayloadValueType.text, + key: '', + value: '', + }) + } + (draft.data as BodyPayload)[0].value = value }) onChange(newBody) }, [onChange, payload]) @@ -136,9 +139,9 @@ const EditBody: FC = ({ )} @@ -148,7 +151,7 @@ const EditBody: FC = ({ instanceId={'http-body-raw'} title={
Raw text
} onChange={handleBodyValueChange} - value={payload.data} + value={stringValue} justVar nodesOutputVars={availableVars} availableNodes={availableNodes} @@ -160,7 +163,7 @@ const EditBody: FC = ({ = ({ /> )} + {/* TODO */} {type === BodyType.binary && ( = { params: '', body: { type: BodyType.none, - data: '', + data: [], }, timeout: { max_connect_timeout: 0, diff --git a/web/app/components/workflow/nodes/http/types.ts b/web/app/components/workflow/nodes/http/types.ts index ecdd29542d..ad92e6786e 100644 --- a/web/app/components/workflow/nodes/http/types.ts +++ b/web/app/components/workflow/nodes/http/types.ts @@ -24,9 +24,21 @@ export type KeyValue = { value: string } +export enum BodyPayloadValueType { + text = 'text', + file = 'file', +} + +export type BodyPayload = { + id?: string + key?: string + type: BodyPayloadValueType + file?: ValueSelector // when type is file + value?: string // when type is text +}[] export type Body = { type: BodyType - data: string + data: string | BodyPayload // string is deprecated, it would convert to BodyPayload after loaded binaryFileVariable?: ValueSelector } diff --git a/web/app/components/workflow/nodes/http/use-config.ts b/web/app/components/workflow/nodes/http/use-config.ts index be73d4e3c4..36293f607e 100644 --- a/web/app/components/workflow/nodes/http/use-config.ts +++ b/web/app/components/workflow/nodes/http/use-config.ts @@ -5,8 +5,9 @@ import useVarList from '../_base/hooks/use-var-list' import { VarType } from '../../types' import type { Var } from '../../types' import { useStore } from '../../store' -import type { Authorization, Body, HttpNodeType, Method, Timeout } from './types' +import { type Authorization, type Body, BodyType, type HttpNodeType, type Method, type Timeout } from './types' import useKeyValueList from './hooks/use-key-value-list' +import { transformToBodyPayload } from './utils' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import useOneStepRun from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run' import { @@ -28,10 +29,15 @@ const useConfig = (id: string, payload: HttpNodeType) => { useEffect(() => { const isReady = defaultConfig && Object.keys(defaultConfig).length > 0 if (isReady) { - setInputs({ + const newInputs = { ...defaultConfig, ...inputs, - }) + } + const bodyData = newInputs.body.data + if (typeof bodyData === 'string') + newInputs.body.data = transformToBodyPayload(bodyData, [BodyType.formData, BodyType.xWwwFormUrlencoded].includes(newInputs.body.type)) + + setInputs(newInputs) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultConfig]) @@ -127,7 +133,7 @@ const useConfig = (id: string, payload: HttpNodeType) => { inputs.url, inputs.headers, inputs.params, - inputs.body.data, + typeof inputs.body.data === 'string' ? inputs.body.data : inputs.body.data.map(item => item.value).join(''), ]) const inputVarValues = (() => { diff --git a/web/app/components/workflow/nodes/http/utils.ts b/web/app/components/workflow/nodes/http/utils.ts index 38f61ae35e..ffb474f45a 100644 --- a/web/app/components/workflow/nodes/http/utils.ts +++ b/web/app/components/workflow/nodes/http/utils.ts @@ -1,5 +1,21 @@ -import type { HttpNodeType } from './types' +import { type BodyPayload, BodyPayloadValueType } from './types' -export const checkNodeValid = (payload: HttpNodeType) => { - return true +export const transformToBodyPayload = (old: string, hasKey: boolean): BodyPayload => { + if (!hasKey) { + return [ + { + type: BodyPayloadValueType.text, + value: old, + }, + ] + } + const bodyPayload = old.split('\n').map((item) => { + const [key, value] = item.split(':') + return { + key: key || '', + type: BodyPayloadValueType.text, + value: value || '', + } + }) + return bodyPayload }