Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
b08327cb
Commit
b08327cb
authored
Feb 28, 2024
by
Joel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: edit body
parent
f1b868d5
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
281 additions
and
42 deletions
+281
-42
code-editor.tsx
...ts/workflow/nodes/_base/components/editor/code-editor.tsx
+1
-0
text-editor.tsx
...ts/workflow/nodes/_base/components/editor/text-editor.tsx
+54
-0
api-input.tsx
...p/components/workflow/nodes/http/components/api-input.tsx
+9
-9
index.tsx
...onents/workflow/nodes/http/components/edit-body/index.tsx
+134
-0
index.tsx
...kflow/nodes/http/components/key-value/bulk-edit/index.tsx
+12
-17
use-key-value-list.ts
...omponents/workflow/nodes/http/hooks/use-key-value-list.ts
+5
-2
mock.ts
web/app/components/workflow/nodes/http/mock.ts
+3
-3
panel.tsx
web/app/components/workflow/nodes/http/panel.tsx
+20
-3
types.ts
web/app/components/workflow/nodes/http/types.ts
+16
-6
use-config.ts
web/app/components/workflow/nodes/http/use-config.ts
+27
-2
No files found.
web/app/components/workflow/nodes/_base/components/editor/code-editor.tsx
View file @
b08327cb
...
...
@@ -7,6 +7,7 @@ type Props = {
value
:
string
onChange
:
(
value
:
string
)
=>
void
title
:
JSX
.
Element
language
?:
string
headerRight
?:
JSX
.
Element
}
...
...
web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx
0 → 100644
View file @
b08327cb
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useCallback
}
from
'react'
import
{
useBoolean
}
from
'ahooks'
import
Base
from
'./base'
type
Props
=
{
value
:
string
onChange
:
(
value
:
string
)
=>
void
title
:
JSX
.
Element
headerRight
?:
JSX
.
Element
minHeight
?:
number
onBlur
?:
()
=>
void
}
const
TextEditor
:
FC
<
Props
>
=
({
value
,
onChange
,
title
,
headerRight
,
minHeight
,
onBlur
,
})
=>
{
const
[
isFocus
,
{
setTrue
:
setIsFocus
,
setFalse
:
setIsNotFocus
,
}]
=
useBoolean
(
false
)
const
handleBlur
=
useCallback
(()
=>
{
setIsNotFocus
()
onBlur
?.()
},
[
setIsNotFocus
,
onBlur
])
return
(
<
div
>
<
Base
title=
{
title
}
value=
{
value
}
headerRight=
{
headerRight
}
isFocus=
{
isFocus
}
minHeight=
{
minHeight
}
>
<
textarea
value=
{
value
}
onChange=
{
e
=>
onChange
(
e
.
target
.
value
)
}
onFocus=
{
setIsFocus
}
onBlur=
{
handleBlur
}
className=
'w-full h-full p-3 resize-none bg-transparent'
/>
</
Base
>
</
div
>
)
}
export
default
React
.
memo
(
TextEditor
)
web/app/components/workflow/nodes/http/components/api-input.tsx
View file @
b08327cb
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useCallback
}
from
'react'
import
{
Method
Enum
}
from
'../types'
import
{
Method
}
from
'../types'
import
Selector
from
'../../_base/components/selector'
import
{
ChevronDown
}
from
'@/app/components/base/icons/src/vender/line/arrows'
const
MethodOptions
=
[
{
label
:
'GET'
,
value
:
Method
Enum
.
get
},
{
label
:
'POST'
,
value
:
Method
Enum
.
post
},
{
label
:
'HEAD'
,
value
:
Method
Enum
.
head
},
{
label
:
'PATCH'
,
value
:
Method
Enum
.
patch
},
{
label
:
'PUT'
,
value
:
Method
Enum
.
put
},
{
label
:
'DELETE'
,
value
:
Method
Enum
.
delete
},
{
label
:
'GET'
,
value
:
Method
.
get
},
{
label
:
'POST'
,
value
:
Method
.
post
},
{
label
:
'HEAD'
,
value
:
Method
.
head
},
{
label
:
'PATCH'
,
value
:
Method
.
patch
},
{
label
:
'PUT'
,
value
:
Method
.
put
},
{
label
:
'DELETE'
,
value
:
Method
.
delete
},
]
type
Props
=
{
readonly
:
boolean
method
:
Method
Enum
onMethodChange
:
(
method
:
Method
Enum
)
=>
void
method
:
Method
onMethodChange
:
(
method
:
Method
)
=>
void
url
:
string
onUrlChange
:
(
url
:
string
)
=>
void
}
...
...
web/app/components/workflow/nodes/http/components/edit-body/index.tsx
0 → 100644
View file @
b08327cb
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useCallback
,
useEffect
}
from
'react'
import
produce
from
'immer'
import
cn
from
'classnames'
import
type
{
Body
}
from
'../../types'
import
{
BodyType
}
from
'../../types'
import
useKeyValueList
from
'../../hooks/use-key-value-list'
import
KeyValue
from
'../key-value'
import
TextEditor
from
'../../../_base/components/editor/text-editor'
import
CodeEditor
from
'../../../_base/components/editor/code-editor'
type
Props
=
{
readonly
:
boolean
payload
:
Body
onChange
:
(
newPayload
:
Body
)
=>
void
}
const
allTypes
=
[
BodyType
.
none
,
BodyType
.
formData
,
BodyType
.
xWwwFormUrlencoded
,
BodyType
.
rawText
,
BodyType
.
json
,
]
const
bodyTextMap
=
{
[
BodyType
.
none
]:
'none'
,
[
BodyType
.
formData
]:
'form-data'
,
[
BodyType
.
xWwwFormUrlencoded
]:
'x-www-form-urlencoded'
,
[
BodyType
.
rawText
]:
'raw text'
,
[
BodyType
.
json
]:
'JSON'
,
}
const
EditBody
:
FC
<
Props
>
=
({
readonly
,
payload
,
onChange
,
})
=>
{
const
{
type
}
=
payload
const
handleTypeChange
=
useCallback
((
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
newType
=
e
.
target
.
value
as
BodyType
onChange
({
type
:
newType
,
data
:
''
,
})
// eslint-disable-next-line @typescript-eslint/no-use-before-define
setBody
([])
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
onChange
])
const
{
list
:
body
,
setList
:
setBody
,
addItem
:
addBody
,
isKeyValueEdit
:
isBodyKeyValueEdit
,
toggleIsKeyValueEdit
:
toggleIsBodyKeyValueEdit
,
}
=
useKeyValueList
(
payload
.
data
)
const
isCurrentKeyValue
=
type
===
BodyType
.
formData
||
type
===
BodyType
.
xWwwFormUrlencoded
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
},
[
body
,
isCurrentKeyValue
])
const
handleBodyValueChange
=
useCallback
((
value
:
string
)
=>
{
const
newBody
=
produce
(
payload
,
(
draft
:
Body
)
=>
{
draft
.
data
=
value
})
onChange
(
newBody
)
},
[
onChange
,
payload
])
return
(
<
div
>
{
/* body type */
}
<
div
className=
'flex flex-wrap'
>
{
allTypes
.
map
(
t
=>
(
<
label
key=
{
t
}
htmlFor=
{
`body-type-${t}`
}
className=
'mr-4 flex items-center h-7 space-x-2'
>
<
input
type=
"radio"
id=
{
`body-type-${t}`
}
value=
{
t
}
checked=
{
type
===
t
}
onChange=
{
handleTypeChange
}
/>
<
div
className=
'leading-[18px] text-[13px] font-normal text-gray-700'
>
{
bodyTextMap
[
t
]
}
</
div
>
</
label
>
))
}
</
div
>
{
/* body value */
}
<
div
className=
{
cn
(
type
!==
BodyType
.
none
&&
'mt-1'
)
}
>
{
type
===
BodyType
.
none
&&
null
}
{
(
type
===
BodyType
.
formData
||
type
===
BodyType
.
xWwwFormUrlencoded
)
&&
(
<
KeyValue
readonly=
{
readonly
}
list=
{
body
}
onChange=
{
setBody
}
onAdd=
{
addBody
}
isKeyValueEdit=
{
isBodyKeyValueEdit
}
toggleKeyValueEdit=
{
toggleIsBodyKeyValueEdit
}
/>
)
}
{
type
===
BodyType
.
rawText
&&
(
<
TextEditor
title=
{
<
div
className=
'uppercase'
>
Raw text
</
div
>
}
onChange=
{
handleBodyValueChange
}
value=
{
payload
.
data
}
minHeight=
{
150
}
/>
)
}
{
type
===
BodyType
.
json
&&
(
<
CodeEditor
title=
{
<
div
className=
'uppercase'
>
JSON
</
div
>
}
value=
{
payload
.
data
}
onChange=
{
handleBodyValueChange
}
/>
)
}
</
div
>
</
div
>
)
}
export
default
React
.
memo
(
EditBody
)
web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx
View file @
b08327cb
...
...
@@ -2,7 +2,7 @@
import
type
{
FC
}
from
'react'
import
React
,
{
useCallback
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
Editor
from
'@/app/components/workflow/nodes/_base/components/editor/base
'
import
TextEditor
from
'@/app/components/workflow/nodes/_base/components/editor/text-editor
'
import
{
LayoutGrid02
}
from
'@/app/components/base/icons/src/vender/line/layout'
const
i18nPrefix
=
'workflow.nodes.http'
...
...
@@ -21,12 +21,14 @@ const BulkEdit: FC<Props> = ({
const
{
t
}
=
useTranslation
()
const
[
tempValue
,
setTempValue
]
=
React
.
useState
(
value
)
const
[
isFocus
,
setIsFocus
]
=
React
.
useState
(
false
)
const
handleChange
=
useCallback
((
e
:
React
.
ChangeEvent
<
HTMLTextAreaElement
>
)
=>
{
setTempValue
(
e
.
target
.
value
)
const
handleChange
=
useCallback
((
value
:
string
)
=>
{
setTempValue
(
value
)
},
[])
const
handleBlur
=
useCallback
(()
=>
{
onChange
(
tempValue
)
},
[
tempValue
,
onChange
])
const
handleSwitchToKeyValueEdit
=
useCallback
(()
=>
{
onChange
(
tempValue
)
onSwitchToKeyValueEdit
()
...
...
@@ -34,9 +36,11 @@ const BulkEdit: FC<Props> = ({
return
(
<
div
>
<
Editor
<
Text
Editor
title=
{
<
div
className=
'uppercase'
>
{
t
(
`${i18nPrefix}.bulkEdit`
)
}
</
div
>
}
value=
{
value
}
value=
{
tempValue
}
onChange=
{
handleChange
}
onBlur=
{
handleBlur
}
headerRight=
{
<
div
className=
'flex items-center h-[18px]'
>
<
div
...
...
@@ -49,17 +53,8 @@ const BulkEdit: FC<Props> = ({
<
div
className=
'ml-3 mr-1.5 w-px h-3 bg-gray-200'
></
div
>
</
div
>
}
isFocus=
{
isFocus
}
minHeight=
{
150
}
>
<
textarea
value=
{
tempValue
}
onChange=
{
handleChange
}
onFocus=
{
()
=>
setIsFocus
(
true
)
}
onBlur=
{
()
=>
setIsFocus
(
false
)
}
className=
'w-full h-full p-3 resize-none bg-transparent'
/>
</
Editor
>
</
div
>
)
}
...
...
web/app/components/workflow/nodes/http/hooks/use-key-value-list.ts
View file @
b08327cb
...
...
@@ -5,18 +5,21 @@ import type { KeyValue } from '../types'
const
strToKeyValueList
=
(
value
:
string
)
=>
{
return
value
.
split
(
'
\
n'
).
map
((
item
)
=>
{
const
[
key
,
value
]
=
item
.
split
(
':'
)
return
{
key
:
key
.
trim
(),
value
:
value
.
trim
()
}
return
{
key
:
key
.
trim
(),
value
:
value
?
.
trim
()
}
})
}
const
useKeyValueList
=
(
value
:
string
)
=>
{
const
[
list
,
setList
]
=
useState
<
KeyValue
[]
>
(
value
?
strToKeyValueList
(
value
)
:
[])
const
addItem
=
useCallback
(()
=>
{
setList
(
prev
=>
[...
prev
,
{
key
:
''
,
value
:
''
}])
},
[])
const
[
isKeyValueEdit
,
{
toggle
:
toggleIsKeyValueEdit
,
}]
=
useBoolean
(
false
)
}]
=
useBoolean
(
true
)
return
{
list
:
list
.
length
===
0
?
[{
key
:
''
,
value
:
''
}]
:
list
,
// no item can not add new item
setList
,
...
...
web/app/components/workflow/nodes/http/mock.ts
View file @
b08327cb
import
{
BlockEnum
}
from
'../../types'
import
{
MethodEnum
}
from
'./types'
import
{
BodyType
,
Method
}
from
'./types'
import
type
{
HttpNodeType
}
from
'./types'
export
const
mockData
:
HttpNodeType
=
{
...
...
@@ -16,12 +16,12 @@ export const mockData: HttpNodeType = {
value_selector
:
[
'bbb'
,
'b'
,
'c'
],
},
],
method
:
Method
Enum
.
get
,
method
:
Method
.
get
,
url
:
'https://api.dify.com/xx'
,
headers
:
'Content-Type: application/json
\
nAccept: */*'
,
params
:
''
,
body
:
{
type
:
'json'
,
type
:
BodyType
.
none
,
data
:
''
,
},
}
web/app/components/workflow/nodes/http/panel.tsx
View file @
b08327cb
...
...
@@ -4,12 +4,12 @@ import useConfig from './use-config'
import
{
mockData
}
from
'./mock'
import
ApiInput
from
'./components/api-input'
import
KeyValue
from
'./components/key-value'
import
EditBody
from
'./components/edit-body'
import
VarList
from
'@/app/components/workflow/nodes/_base/components/variable/var-list'
import
Field
from
'@/app/components/workflow/nodes/_base/components/field'
import
AddButton
from
'@/app/components/base/button/add-button'
import
Split
from
'@/app/components/workflow/nodes/_base/components/split'
import
OutputVars
,
{
VarItem
}
from
'@/app/components/workflow/nodes/_base/components/output-vars'
const
i18nPrefix
=
'workflow.nodes.http'
const
Panel
:
FC
=
()
=>
{
...
...
@@ -27,6 +27,12 @@ const Panel: FC = () => {
addHeader
,
isHeaderKeyValueEdit
,
toggleIsHeaderKeyValueEdit
,
params
,
setParams
,
addParam
,
isParamKeyValueEdit
,
toggleIsParamKeyValueEdit
,
setBody
,
}
=
useConfig
(
mockData
)
return
(
...
...
@@ -70,12 +76,23 @@ const Panel: FC = () => {
<
Field
title=
{
t
(
`${i18nPrefix}.params`
)
}
>
params
<
KeyValue
list=
{
params
}
onChange=
{
setParams
}
onAdd=
{
addParam
}
readonly=
{
readOnly
}
isKeyValueEdit=
{
isParamKeyValueEdit
}
toggleKeyValueEdit=
{
toggleIsParamKeyValueEdit
}
/>
</
Field
>
<
Field
title=
{
t
(
`${i18nPrefix}.body`
)
}
>
body
<
EditBody
readonly=
{
readOnly
}
payload=
{
inputs
.
body
}
onChange=
{
setBody
}
/>
</
Field
>
</
div
>
<
Split
/>
...
...
web/app/components/workflow/nodes/http/types.ts
View file @
b08327cb
import
type
{
CommonNodeType
,
Variable
}
from
'@/app/components/workflow/types'
export
enum
Method
Enum
{
export
enum
Method
{
get
=
'get'
,
post
=
'post'
,
head
=
'head'
,
...
...
@@ -9,19 +9,29 @@ export enum MethodEnum {
delete
=
'delete'
,
}
export
enum
BodyType
{
none
=
'none'
,
formData
=
'form-data'
,
xWwwFormUrlencoded
=
'x-www-form-urlencoded'
,
rawText
=
'raw-text'
,
json
=
'json'
,
}
export
type
KeyValue
=
{
key
:
string
value
:
string
}
export
type
Body
=
{
type
:
BodyType
data
:
string
}
export
type
HttpNodeType
=
CommonNodeType
&
{
variables
:
Variable
[]
method
:
Method
Enum
method
:
Method
url
:
string
headers
:
string
params
:
string
body
:
{
type
:
string
data
:
string
}
body
:
Body
}
web/app/components/workflow/nodes/http/use-config.ts
View file @
b08327cb
import
{
useCallback
,
useState
}
from
'react'
import
produce
from
'immer'
import
useVarList
from
'../_base/hooks/use-var-list'
import
type
{
HttpNodeType
,
MethodEnum
}
from
'./types'
import
type
{
Body
,
HttpNodeType
,
Method
}
from
'./types'
import
useKeyValueList
from
'./hooks/use-key-value-list'
const
useConfig
=
(
initInputs
:
HttpNodeType
)
=>
{
const
[
inputs
,
setInputs
]
=
useState
<
HttpNodeType
>
(
initInputs
)
...
...
@@ -10,7 +11,7 @@ const useConfig = (initInputs: HttpNodeType) => {
setInputs
,
})
const
handleMethodChange
=
useCallback
((
method
:
Method
Enum
)
=>
{
const
handleMethodChange
=
useCallback
((
method
:
Method
)
=>
{
setInputs
(
prev
=>
({
...
prev
,
method
,
...
...
@@ -32,17 +33,41 @@ const useConfig = (initInputs: HttpNodeType) => {
toggleIsKeyValueEdit
:
toggleIsHeaderKeyValueEdit
,
}
=
useKeyValueList
(
inputs
.
headers
)
const
{
list
:
params
,
setList
:
setParams
,
addItem
:
addParam
,
isKeyValueEdit
:
isParamKeyValueEdit
,
toggleIsKeyValueEdit
:
toggleIsParamKeyValueEdit
,
}
=
useKeyValueList
(
inputs
.
params
)
const
setBody
=
useCallback
((
data
:
Body
)
=>
{
const
newInputs
=
produce
(
inputs
,
(
draft
:
HttpNodeType
)
=>
{
draft
.
body
=
data
})
setInputs
(
newInputs
)
},
[
inputs
,
setInputs
])
return
{
inputs
,
handleVarListChange
,
handleAddVariable
,
handleMethodChange
,
handleUrlChange
,
// headers
headers
,
setHeaders
,
addHeader
,
isHeaderKeyValueEdit
,
toggleIsHeaderKeyValueEdit
,
// params
params
,
setParams
,
addParam
,
isParamKeyValueEdit
,
toggleIsParamKeyValueEdit
,
// body
setBody
,
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment