fix: where HTTP node URL, JSON text, and raw text template rendering could not find the corresponding rendering variables (#745)
This commit is contained in:
parent
ff00dcb31b
commit
4ff734f15f
|
|
@ -4655,7 +4655,7 @@ func TestJsonSerializationDeserializationWithWarning(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSetAppVariablesFOrSubProcesses(t *testing.T) {
|
||||
func TestSetAppVariablesForSubProcesses(t *testing.T) {
|
||||
mockey.PatchConvey("app variables for sub_process", t, func() {
|
||||
r := newWfTestRunner(t)
|
||||
defer r.closeFn()
|
||||
|
|
@ -4672,3 +4672,79 @@ func TestSetAppVariablesFOrSubProcesses(t *testing.T) {
|
|||
|
||||
})
|
||||
}
|
||||
|
||||
func TestHttpImplicitDependencies(t *testing.T) {
|
||||
mockey.PatchConvey("test http implicit dependencies", t, func() {
|
||||
r := newWfTestRunner(t)
|
||||
defer r.closeFn()
|
||||
|
||||
r.appVarS.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return("1.0", nil).AnyTimes()
|
||||
|
||||
idStr := r.load("httprequester/http_implicit_dependencies.json")
|
||||
|
||||
r.publish(idStr, "v0.0.1", true)
|
||||
|
||||
runner := mockcode.NewMockRunner(r.ctrl)
|
||||
runner.EXPECT().Run(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, request *coderunner.RunRequest) (*coderunner.RunResponse, error) {
|
||||
in := request.Params["input"]
|
||||
_ = in
|
||||
result := make(map[string]any)
|
||||
err := sonic.UnmarshalString(in.(string), &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &coderunner.RunResponse{
|
||||
Result: result,
|
||||
}, nil
|
||||
}).AnyTimes()
|
||||
|
||||
code.SetCodeRunner(runner)
|
||||
|
||||
mockey.PatchConvey("test http node implicit dependencies", func() {
|
||||
input := map[string]string{
|
||||
"input": "a",
|
||||
}
|
||||
result, _ := r.openapiSyncRun(idStr, input)
|
||||
|
||||
batchRets := result["batch"].([]any)
|
||||
loopRets := result["loop"].([]any)
|
||||
|
||||
for _, r := range batchRets {
|
||||
assert.Contains(t, []any{
|
||||
"http://echo.apifox.com/anything?aa=1.0&cc=1",
|
||||
"http://echo.apifox.com/anything?aa=1.0&cc=2",
|
||||
}, r)
|
||||
}
|
||||
for _, r := range loopRets {
|
||||
assert.Contains(t, []any{
|
||||
"http://echo.apifox.com/anything?a=1&m=123",
|
||||
"http://echo.apifox.com/anything?a=2&m=123",
|
||||
}, r)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
mockey.PatchConvey("node debug http node implicit dependencies", func() {
|
||||
exeID := r.nodeDebug(idStr, "109387",
|
||||
withNDInput(map[string]string{
|
||||
"__apiInfo_url_87fc7c69536cae843fa7f5113cf0067b": "m",
|
||||
"__apiInfo_url_ac86361e3cd503952e71986dc091fa6f": "a",
|
||||
"__body_bodyData_json_ac86361e3cd503952e71986dc091fa6f": "b",
|
||||
"__body_bodyData_json_f77817a7cf8441279e1cfd8af4eeb1da": "1",
|
||||
}))
|
||||
|
||||
e := r.getProcess(idStr, exeID, withSpecificNodeID("109387"))
|
||||
e.assertSuccess()
|
||||
|
||||
ret := make(map[string]any)
|
||||
err := sonic.UnmarshalString(e.output, &ret)
|
||||
assert.Nil(t, err)
|
||||
err = sonic.UnmarshalString(ret["body"].(string), &ret)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ret["url"].(string), "http://echo.apifox.com/anything?a=a&m=m")
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ func CanvasBlockInputToTypeInfo(b *vo.BlockInput) (tInfo *vo.TypeInfo, err error
|
|||
}
|
||||
tInfo.Properties[subV.Name] = subTInfo
|
||||
} else if b.Value.Type == vo.BlockInputValueTypeObjectRef {
|
||||
subV, err := parseParam(subVAny)
|
||||
subV, err := ParseParam(subVAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ func CanvasBlockInputToFieldInfo(b *vo.BlockInput, path einoCompose.FieldPath, p
|
|||
|
||||
for i := range paramList {
|
||||
paramAny := paramList[i]
|
||||
param, err := parseParam(paramAny)
|
||||
param, err := ParseParam(paramAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -343,7 +343,7 @@ func parseBlockInputRef(content any) (*vo.BlockInputReference, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
func parseParam(v any) (*vo.Param, error) {
|
||||
func ParseParam(v any) (*vo.Param, error) {
|
||||
if pa, ok := v.(*vo.Param); ok {
|
||||
return pa, nil
|
||||
}
|
||||
|
|
@ -497,7 +497,7 @@ func SetOutputTypesForNodeSchema(n *vo.Node, ns *schema.NodeSchema) error {
|
|||
|
||||
func SetOutputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema) error {
|
||||
for _, vAny := range n.Data.Outputs {
|
||||
param, err := parseParam(vAny)
|
||||
param, err := ParseParam(vAny)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -565,7 +565,7 @@ func BlockInputToNamedTypeInfo(name string, b *vo.BlockInput) (*vo.NamedTypeInfo
|
|||
}
|
||||
tInfo.Properties = append(tInfo.Properties, subNInfo)
|
||||
} else if b.Value.Type == vo.BlockInputValueTypeObjectRef {
|
||||
subV, err := parseParam(subVAny)
|
||||
subV, err := ParseParam(subVAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,578 @@
|
|||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "100001",
|
||||
"type": "1",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 180,
|
||||
"y": 26.950000000000003
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
|
||||
"subTitle": "",
|
||||
"title": "开始"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "input",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"trigger_parameters": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "900001",
|
||||
"type": "2",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2880,
|
||||
"y": 13.950000000000003
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
|
||||
"subTitle": "",
|
||||
"title": "结束"
|
||||
},
|
||||
"inputs": {
|
||||
"terminatePlan": "returnVariables",
|
||||
"inputParameters": [
|
||||
{
|
||||
"name": "batch",
|
||||
"input": {
|
||||
"type": "list",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "125743",
|
||||
"name": "output"
|
||||
},
|
||||
"rawMeta": {
|
||||
"type": 99
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "loop",
|
||||
"input": {
|
||||
"type": "list",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "122230",
|
||||
"name": "output"
|
||||
},
|
||||
"rawMeta": {
|
||||
"type": 99
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "122230",
|
||||
"type": "21",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 941.8355065195586,
|
||||
"y": -65.71715145436308
|
||||
},
|
||||
"canvasPosition": {
|
||||
"x": 560,
|
||||
"y": 319.9
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"title": "循环",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Loop-v2.jpg",
|
||||
"description": "用于通过设定循环次数和逻辑,重复执行一系列任务",
|
||||
"mainColor": "#00B2B2",
|
||||
"subTitle": "循环"
|
||||
},
|
||||
"inputs": {
|
||||
"loopType": "array",
|
||||
"loopCount": {
|
||||
"type": "integer",
|
||||
"value": {
|
||||
"type": "literal",
|
||||
"content": "10"
|
||||
}
|
||||
},
|
||||
"variableParameters": [
|
||||
{
|
||||
"name": "m",
|
||||
"input": {
|
||||
"type": "string",
|
||||
"value": {
|
||||
"type": "literal",
|
||||
"content": "123",
|
||||
"rawMeta": {
|
||||
"type": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputParameters": [
|
||||
{
|
||||
"name": "input",
|
||||
"input": {
|
||||
"type": "list",
|
||||
"value": {
|
||||
"type": "literal",
|
||||
"content": "[\"1\",\"2\"]",
|
||||
"rawMeta": {
|
||||
"type": 99
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "output",
|
||||
"input": {
|
||||
"type": "list",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "164858",
|
||||
"name": "url"
|
||||
},
|
||||
"rawMeta": {
|
||||
"type": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"id": "109387",
|
||||
"type": "45",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 180,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"title": "HTTP 请求",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-HTTP.png",
|
||||
"description": "用于发送API请求,从接口返回数据",
|
||||
"mainColor": "#3071F2",
|
||||
"subTitle": "HTTP 请求"
|
||||
},
|
||||
"inputParameters": [],
|
||||
"inputs": {
|
||||
"apiInfo": {
|
||||
"method": "GET",
|
||||
"url": "http://echo.apifox.com/anything?a={{block_output_122230.input}}&m={{block_output_122230.m}}"
|
||||
},
|
||||
"body": {
|
||||
"bodyType": "JSON",
|
||||
"bodyData": {
|
||||
"binary": {
|
||||
"fileURL": {
|
||||
"type": "string",
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "",
|
||||
"name": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"json": "{\n \"index\": {{block_output_122230.index}},\n \"value\": \"{{block_output_122230.input}}\"\n}"
|
||||
}
|
||||
},
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"auth": {
|
||||
"authType": "BEARER_AUTH",
|
||||
"authData": {
|
||||
"customData": {
|
||||
"addTo": "header"
|
||||
}
|
||||
},
|
||||
"authOpen": false
|
||||
},
|
||||
"setting": {
|
||||
"timeout": 120,
|
||||
"retryTimes": 3
|
||||
},
|
||||
"settingOnError": {}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "body"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "statusCode"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "headers"
|
||||
}
|
||||
],
|
||||
"settingOnError": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "164858",
|
||||
"type": "5",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 640,
|
||||
"y": 12
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"title": "代码_1",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Code-v2.jpg",
|
||||
"description": "编写代码,处理输入变量来生成返回值",
|
||||
"mainColor": "#00B2B2",
|
||||
"subTitle": "代码"
|
||||
},
|
||||
"inputs": {
|
||||
"inputParameters": [
|
||||
{
|
||||
"name": "input",
|
||||
"input": {
|
||||
"type": "string",
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "109387",
|
||||
"name": "body"
|
||||
},
|
||||
"rawMeta": {
|
||||
"type": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"code": "import json\nasync def main(args: Args) -> Output:\n params = args.params\n input_string = params['input']\n return json.loads(input_string)",
|
||||
"language": 3,
|
||||
"settingOnError": {
|
||||
"processType": 1,
|
||||
"timeoutMs": 60000,
|
||||
"retryTimes": 0
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "args",
|
||||
"schema": []
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "url"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"sourceNodeID": "122230",
|
||||
"targetNodeID": "109387",
|
||||
"sourcePortID": "loop-function-inline-output"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "109387",
|
||||
"targetNodeID": "164858"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "164858",
|
||||
"targetNodeID": "122230",
|
||||
"targetPortID": "loop-function-inline-input"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "125743",
|
||||
"type": "28",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 2090,
|
||||
"y": 13
|
||||
},
|
||||
"canvasPosition": {
|
||||
"x": 1680,
|
||||
"y": 306.9
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"title": "批处理",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Batch-v2.jpg",
|
||||
"description": "通过设定批量运行次数和逻辑,运行批处理体内的任务",
|
||||
"mainColor": "#00B2B2",
|
||||
"subTitle": "批处理"
|
||||
},
|
||||
"inputs": {
|
||||
"concurrentSize": {
|
||||
"type": "integer",
|
||||
"value": {
|
||||
"type": "literal",
|
||||
"content": "10"
|
||||
}
|
||||
},
|
||||
"batchSize": {
|
||||
"type": "integer",
|
||||
"value": {
|
||||
"type": "literal",
|
||||
"content": "100"
|
||||
}
|
||||
},
|
||||
"inputParameters": [
|
||||
{
|
||||
"name": "input",
|
||||
"input": {
|
||||
"type": "list",
|
||||
"value": {
|
||||
"type": "literal",
|
||||
"content": "[\"1\",\"2\"]",
|
||||
"rawMeta": {
|
||||
"type": 99
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "output",
|
||||
"input": {
|
||||
"type": "list",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "182572",
|
||||
"name": "url"
|
||||
},
|
||||
"rawMeta": {
|
||||
"type": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"id": "191311",
|
||||
"type": "45",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 180,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"title": "HTTP 请求_1",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-HTTP.png",
|
||||
"description": "用于发送API请求,从接口返回数据",
|
||||
"mainColor": "#3071F2",
|
||||
"subTitle": "HTTP 请求"
|
||||
},
|
||||
"inputParameters": [],
|
||||
"inputs": {
|
||||
"apiInfo": {
|
||||
"method": "GET",
|
||||
"url": "http://echo.apifox.com/anything?cc={{block_output_125743.input}}&aa={{global_variable_app[\"app\"]}}"
|
||||
},
|
||||
"body": {
|
||||
"bodyType": "JSON",
|
||||
"bodyData": {
|
||||
"binary": {
|
||||
"fileURL": {
|
||||
"type": "string",
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "",
|
||||
"name": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"json": "{\n \"value\": {{block_output_125743.index}}\n}"
|
||||
}
|
||||
},
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"auth": {
|
||||
"authType": "BEARER_AUTH",
|
||||
"authData": {
|
||||
"customData": {
|
||||
"addTo": "header"
|
||||
}
|
||||
},
|
||||
"authOpen": false
|
||||
},
|
||||
"setting": {
|
||||
"timeout": 120,
|
||||
"retryTimes": 3
|
||||
},
|
||||
"settingOnError": {}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "body"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "statusCode"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "headers"
|
||||
}
|
||||
],
|
||||
"settingOnError": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "182572",
|
||||
"type": "5",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 640,
|
||||
"y": 12
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"nodeMeta": {
|
||||
"title": "代码_3",
|
||||
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Code-v2.jpg",
|
||||
"description": "编写代码,处理输入变量来生成返回值",
|
||||
"mainColor": "#00B2B2",
|
||||
"subTitle": "代码"
|
||||
},
|
||||
"inputs": {
|
||||
"inputParameters": [
|
||||
{
|
||||
"name": "input",
|
||||
"input": {
|
||||
"type": "string",
|
||||
"value": {
|
||||
"type": "ref",
|
||||
"content": {
|
||||
"source": "block-output",
|
||||
"blockID": "191311",
|
||||
"name": "body"
|
||||
},
|
||||
"rawMeta": {
|
||||
"type": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"code": "import json\nasync def main(args: Args) -> Output:\n params = args.params\n input_string = params['input']\n return json.loads(input_string)",
|
||||
"language": 3,
|
||||
"settingOnError": {
|
||||
"processType": 1,
|
||||
"timeoutMs": 60000,
|
||||
"retryTimes": 0
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "url"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"sourceNodeID": "125743",
|
||||
"targetNodeID": "191311",
|
||||
"sourcePortID": "batch-function-inline-output"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "191311",
|
||||
"targetNodeID": "182572"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "182572",
|
||||
"targetNodeID": "125743",
|
||||
"targetPortID": "batch-function-inline-input"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"sourceNodeID": "100001",
|
||||
"targetNodeID": "122230"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "125743",
|
||||
"targetNodeID": "900001",
|
||||
"sourcePortID": "batch-output"
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "122230",
|
||||
"targetNodeID": "125743",
|
||||
"sourcePortID": "loop-output"
|
||||
}
|
||||
],
|
||||
"versions": {
|
||||
"loop": "v2"
|
||||
}
|
||||
}
|
||||
|
|
@ -23,10 +23,12 @@ import (
|
|||
|
||||
"github.com/cloudwego/eino/compose"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/canvas/convert"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/schema"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/crypto"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
)
|
||||
|
||||
var extractBracesRegexp = regexp.MustCompile(`\{\{(.*?)}}`)
|
||||
|
|
@ -43,9 +45,10 @@ func extractBracesContent(s string) []string {
|
|||
}
|
||||
|
||||
type ImplicitNodeDependency struct {
|
||||
NodeID string
|
||||
FieldPath compose.FieldPath
|
||||
TypeInfo *vo.TypeInfo
|
||||
NodeID string
|
||||
FieldPath compose.FieldPath
|
||||
TypeInfo *vo.TypeInfo
|
||||
IsIntermediateVar bool
|
||||
}
|
||||
|
||||
func extractImplicitDependency(node *vo.Node, canvas *vo.Canvas) ([]*ImplicitNodeDependency, error) {
|
||||
|
|
@ -84,14 +87,24 @@ func extractImplicitDependency(node *vo.Node, canvas *vo.Canvas) ([]*ImplicitNod
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if node.Data.Inputs.Body.BodyType == string(BodyTypeRawText) {
|
||||
rawTextVars := extractBracesContent(node.Data.Inputs.Body.BodyData.Json)
|
||||
rawTextVars := extractBracesContent(node.Data.Inputs.Body.BodyData.RawText)
|
||||
err = extractDependenciesFromVars(rawTextVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nodeID2ParentID := make(map[string]string)
|
||||
for _, n := range canvas.Nodes {
|
||||
if len(n.Blocks) > 0 {
|
||||
for _, block := range n.Blocks {
|
||||
nodeID2ParentID[block.ID] = n.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nodeFinder func(nodes []*vo.Node, nodeID string) *vo.Node
|
||||
nodeFinder = func(nodes []*vo.Node, nodeID string) *vo.Node {
|
||||
for i := range nodes {
|
||||
|
|
@ -111,23 +124,78 @@ func extractImplicitDependency(node *vo.Node, canvas *vo.Canvas) ([]*ImplicitNod
|
|||
if fNode == nil {
|
||||
continue
|
||||
}
|
||||
tInfoMap := make(map[string]*vo.TypeInfo, len(node.Data.Outputs))
|
||||
for _, vAny := range fNode.Data.Outputs {
|
||||
v, err := vo.ParseVariable(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
intermediateVars := map[string]bool{}
|
||||
tInfoMap := make(map[string]*vo.TypeInfo)
|
||||
parentID, ok := nodeID2ParentID[node.ID]
|
||||
if ok && parentID == fNode.ID {
|
||||
// when referencing a composite node, the index field type is available by default
|
||||
tInfoMap["index"] = &vo.TypeInfo{
|
||||
Type: vo.DataTypeInteger,
|
||||
}
|
||||
tInfo, err := convert.CanvasVariableToTypeInfo(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, p := range fNode.Data.Inputs.InputParameters {
|
||||
tInfo, err := convert.CanvasBlockInputToTypeInfo(p.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tInfo.Type != vo.DataTypeArray {
|
||||
return nil, fmt.Errorf("when referencing a composite node, the input parameter must be an array type")
|
||||
}
|
||||
tInfoMap[p.Name] = tInfo.ElemTypeInfo
|
||||
}
|
||||
|
||||
if fNode.Data.Inputs.Loop != nil {
|
||||
for _, vAny := range fNode.Data.Inputs.Loop.VariableParameters {
|
||||
v, err := convert.ParseParam(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfo, err := convert.CanvasBlockInputToTypeInfo(v.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
|
||||
intermediateVars[v.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
} else if entity.NodeTypeMetas[entity.IDStrToNodeType(fNode.Type)].IsComposite {
|
||||
for _, vAny := range fNode.Data.Outputs {
|
||||
v, err := convert.ParseParam(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfo, err := convert.CanvasBlockInputToTypeInfo(v.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
}
|
||||
} else {
|
||||
for _, vAny := range fNode.Data.Outputs {
|
||||
v, err := vo.ParseVariable(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfo, err := convert.CanvasVariableToTypeInfo(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
}
|
||||
|
||||
tInfo, ok := getTypeInfoByPath(ds.FieldPath[0], ds.FieldPath[1:], tInfoMap)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot find type info for dependency: %s", ds.FieldPath)
|
||||
}
|
||||
|
||||
ds.TypeInfo = tInfo
|
||||
if len(intermediateVars) > 0 {
|
||||
ds.IsIntermediateVar = intermediateVars[strings.Join(ds.FieldPath, ".")]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return dependencies, nil
|
||||
|
|
@ -152,7 +220,7 @@ var globalVariableRegex = regexp.MustCompile(`global_variable_\w+\s*\["(.*?)"]`)
|
|||
func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, implicitNodeDependencies []*ImplicitNodeDependency) (err error) {
|
||||
inputs := n.Data.Inputs
|
||||
implicitPathVars := make(map[string]bool)
|
||||
addImplicitVarsSources := func(prefix string, vars []string) error {
|
||||
addImplicitVarsSources := func(prefix string, vars []string, parent *vo.Node) error {
|
||||
for _, v := range vars {
|
||||
if strings.HasPrefix(v, "block_output_") {
|
||||
paths := strings.Split(strings.TrimPrefix(v, "block_output_"), ".")
|
||||
|
|
@ -167,7 +235,8 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
|||
}
|
||||
implicitPathVars[pathValue] = true
|
||||
ns.SetInputType(pathValue, dep.TypeInfo)
|
||||
ns.AddInputSource(&vo.FieldInfo{
|
||||
|
||||
filedInfo := &vo.FieldInfo{
|
||||
Path: []string{pathValue},
|
||||
Source: vo.FieldSource{
|
||||
Ref: &vo.Reference{
|
||||
|
|
@ -175,7 +244,12 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
|||
FromPath: dep.FieldPath,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if dep.IsIntermediateVar && parent != nil {
|
||||
filedInfo.Source.Ref.VariableType = ptr.Of(vo.ParentIntermediate)
|
||||
}
|
||||
ns.AddInputSource(filedInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -215,7 +289,7 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
|||
}
|
||||
|
||||
urlVars := extractBracesContent(inputs.APIInfo.URL)
|
||||
err = addImplicitVarsSources("__apiInfo_url_", urlVars)
|
||||
err = addImplicitVarsSources("__apiInfo_url_", urlVars, n.Parent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -304,13 +378,13 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
|||
ns.AddInputSource(sources...)
|
||||
case BodyTypeJSON:
|
||||
jsonVars := extractBracesContent(inputs.Body.BodyData.Json)
|
||||
err = addImplicitVarsSources("__body_bodyData_json_", jsonVars)
|
||||
err = addImplicitVarsSources("__body_bodyData_json_", jsonVars, n.Parent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case BodyTypeRawText:
|
||||
rawTextVars := extractBracesContent(inputs.Body.BodyData.RawText)
|
||||
err = addImplicitVarsSources("__body_bodyData_rawText_", rawTextVars)
|
||||
err = addImplicitVarsSources("__body_bodyData_rawText_", rawTextVars, n.Parent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,6 +360,11 @@ type HTTPRequester struct {
|
|||
md5FieldMapping MD5FieldMapping
|
||||
}
|
||||
|
||||
func adaptTemplate(template string) string {
|
||||
return globalVariableReplaceRegexp.ReplaceAllString(template, "global_variable_$1.$2")
|
||||
|
||||
}
|
||||
|
||||
func (hg *HTTPRequester) Invoke(ctx context.Context, input map[string]any) (output map[string]any, err error) {
|
||||
var (
|
||||
req = &Request{}
|
||||
|
|
@ -380,7 +385,7 @@ func (hg *HTTPRequester) Invoke(ctx context.Context, input map[string]any) (outp
|
|||
Header: http.Header{},
|
||||
}
|
||||
|
||||
httpURL, err := nodes.TemplateRender(hg.urlConfig.Tpl, req.URLVars)
|
||||
httpURL, err := nodes.TemplateRender(adaptTemplate(hg.urlConfig.Tpl), req.URLVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -524,7 +529,7 @@ func (b *BodyConfig) getBodyAndContentType(ctx context.Context, req *Request) (i
|
|||
|
||||
switch b.BodyType {
|
||||
case BodyTypeJSON:
|
||||
jsonString, err := nodes.TemplateRender(b.TextJsonConfig.Tpl, req.JsonVars)
|
||||
jsonString, err := nodes.TemplateRender(adaptTemplate(b.TextJsonConfig.Tpl), req.JsonVars)
|
||||
if err != nil {
|
||||
return nil, contentType, err
|
||||
}
|
||||
|
|
@ -539,7 +544,7 @@ func (b *BodyConfig) getBodyAndContentType(ctx context.Context, req *Request) (i
|
|||
body = strings.NewReader(form.Encode())
|
||||
contentType = ContentTypeFormURLEncoded
|
||||
case BodyTypeRawText:
|
||||
textString, err := nodes.TemplateRender(b.TextPlainConfig.Tpl, req.TextPlainVars)
|
||||
textString, err := nodes.TemplateRender(adaptTemplate(b.TextPlainConfig.Tpl), req.TextPlainVars)
|
||||
if err != nil {
|
||||
return nil, contentType, err
|
||||
}
|
||||
|
|
@ -632,7 +637,7 @@ func (hg *HTTPRequester) ToCallbackInput(_ context.Context, input map[string]any
|
|||
result := make(map[string]any)
|
||||
result["method"] = hg.method
|
||||
|
||||
u, err := nodes.TemplateRender(hg.urlConfig.Tpl, request.URLVars)
|
||||
u, err := nodes.TemplateRender(adaptTemplate(hg.urlConfig.Tpl), request.URLVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -666,7 +671,7 @@ func (hg *HTTPRequester) ToCallbackInput(_ context.Context, input map[string]any
|
|||
result["body"] = nil
|
||||
switch hg.bodyConfig.BodyType {
|
||||
case BodyTypeJSON:
|
||||
js, err := nodes.TemplateRender(hg.bodyConfig.TextJsonConfig.Tpl, request.JsonVars)
|
||||
js, err := nodes.TemplateRender(adaptTemplate(hg.bodyConfig.TextJsonConfig.Tpl), request.JsonVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -677,7 +682,7 @@ func (hg *HTTPRequester) ToCallbackInput(_ context.Context, input map[string]any
|
|||
}
|
||||
result["body"] = ret
|
||||
case BodyTypeRawText:
|
||||
tx, err := nodes.TemplateRender(hg.bodyConfig.TextPlainConfig.Tpl, request.TextPlainVars)
|
||||
tx, err := nodes.TemplateRender(adaptTemplate(hg.bodyConfig.TextPlainConfig.Tpl), request.TextPlainVars)
|
||||
if err != nil {
|
||||
|
||||
return nil, err
|
||||
|
|
@ -729,7 +734,7 @@ func (hg *HTTPRequester) parserToRequest(input map[string]any) (*Request, error)
|
|||
if strings.HasPrefix(urlKey, "global_variable_") {
|
||||
urlKey = globalVariableReplaceRegexp.ReplaceAllString(urlKey, "global_variable_$1.$2")
|
||||
}
|
||||
nodes.SetMapValue(request.URLVars, strings.Split(urlKey, "."), value.(string))
|
||||
nodes.SetMapValue(request.URLVars, strings.Split(urlKey, "."), value)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(key, headersPrefix) {
|
||||
|
|
@ -777,7 +782,6 @@ func (hg *HTTPRequester) parserToRequest(input map[string]any) (*Request, error)
|
|||
if formDataKey, ok := hg.md5FieldMapping.BodyMD5Mapping[formDataMd5Key]; ok {
|
||||
request.FormDataVars[formDataKey] = value.(string)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if strings.HasPrefix(bodyKey, bodyFormURLEncodedPrefix) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue