feat: support http body to new data struct

This commit is contained in:
Joel 2024-08-28 16:56:31 +08:00
parent e90b055c47
commit 41f0ce1012
5 changed files with 91 additions and 53 deletions

View File

@ -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<Props> = ({
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<Props> = ({
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<Props> = ({
<KeyValue
readonly={readonly}
nodeId={nodeId}
list={body}
onChange={setBody}
onAdd={addBody}
list={bodyPayload as KeyValueType[]}
onChange={handleBodyPayloadChange}
onAdd={handleAddBody}
isSupportFile={type === BodyType.formData}
/>
)}
@ -148,7 +151,7 @@ const EditBody: FC<Props> = ({
instanceId={'http-body-raw'}
title={<div className='uppercase'>Raw text</div>}
onChange={handleBodyValueChange}
value={payload.data}
value={stringValue}
justVar
nodesOutputVars={availableVars}
availableNodes={availableNodes}
@ -160,7 +163,7 @@ const EditBody: FC<Props> = ({
<InputWithVar
instanceId={'http-body-json'}
title='JSON'
value={payload.data}
value={stringValue}
onChange={handleBodyValueChange}
justVar
nodesOutputVars={availableVars}
@ -169,6 +172,7 @@ const EditBody: FC<Props> = ({
/>
)}
{/* TODO */}
{type === BodyType.binary && (
<VarReferencePicker
nodeId={nodeId}

View File

@ -16,7 +16,7 @@ const nodeDefault: NodeDefault<HttpNodeType> = {
params: '',
body: {
type: BodyType.none,
data: '',
data: [],
},
timeout: {
max_connect_timeout: 0,

View File

@ -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
}

View File

@ -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 = (() => {

View File

@ -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
}