Skip to content

Commit 761ee78

Browse files
Add method to run any node with input variables
Co-Authored-By: Chris Li <chris.li.2046@gmail.com>
1 parent a354403 commit 761ee78

3 files changed

Lines changed: 388 additions & 0 deletions

File tree

core/taskengine/engine.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,3 +1060,76 @@ func (n *Engine) GetWorkflowCount(user *model.User, payload *avsproto.GetWorkflo
10601060
Total: total,
10611061
}, nil
10621062
}
1063+
1064+
func (n *Engine) RunNodeWithInputs(nodeType string, nodeConfig map[string]interface{}, inputVariables map[string]interface{}) (map[string]interface{}, error) {
1065+
vm, err := NewVMWithData(nil, nil, n.smartWalletConfig, nil)
1066+
if err != nil {
1067+
return nil, err
1068+
}
1069+
1070+
vm.WithLogger(n.logger).WithDb(n.db)
1071+
1072+
node, err := CreateNodeFromType(nodeType, nodeConfig, "")
1073+
if err != nil {
1074+
return nil, err
1075+
}
1076+
1077+
// Run the node with input variables
1078+
executionStep, err := vm.RunNodeWithInputs(node, inputVariables)
1079+
if err != nil {
1080+
return nil, err
1081+
}
1082+
1083+
if !executionStep.Success {
1084+
return nil, fmt.Errorf("execution failed: %s", executionStep.Error)
1085+
}
1086+
1087+
result := make(map[string]interface{})
1088+
1089+
switch nodeType {
1090+
case "blockTrigger":
1091+
if codeOutput := executionStep.GetCustomCode(); codeOutput != nil && codeOutput.Data != nil {
1092+
var data map[string]interface{}
1093+
if err := json.Unmarshal(codeOutput.Data.Value, &data); err == nil {
1094+
result = data
1095+
}
1096+
}
1097+
case "restApi":
1098+
if restOutput := executionStep.GetRestApi(); restOutput != nil && restOutput.Data != nil {
1099+
var data map[string]interface{}
1100+
if err := json.Unmarshal(restOutput.Data.Value, &data); err == nil {
1101+
result["data"] = data
1102+
} else {
1103+
result["data"] = restOutput.Data
1104+
}
1105+
}
1106+
case "contractRead":
1107+
if readOutput := executionStep.GetContractRead(); readOutput != nil {
1108+
result["data"] = readOutput.Data
1109+
}
1110+
case "customCode":
1111+
if codeOutput := executionStep.GetCustomCode(); codeOutput != nil && codeOutput.Data != nil {
1112+
var data map[string]interface{}
1113+
if err := json.Unmarshal(codeOutput.Data.Value, &data); err == nil {
1114+
result = data
1115+
} else {
1116+
result["data"] = codeOutput.Data
1117+
}
1118+
}
1119+
case "branch":
1120+
if branchOutput := executionStep.GetBranch(); branchOutput != nil {
1121+
result["conditionId"] = branchOutput.ConditionId
1122+
}
1123+
case "filter":
1124+
if filterOutput := executionStep.GetFilter(); filterOutput != nil && filterOutput.Data != nil {
1125+
var data interface{}
1126+
if err := json.Unmarshal(filterOutput.Data.Value, &data); err == nil {
1127+
result["data"] = data
1128+
} else {
1129+
result["data"] = filterOutput.Data
1130+
}
1131+
}
1132+
}
1133+
1134+
return result, nil
1135+
}

core/taskengine/run_node_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package taskengine
2+
3+
import (
4+
"testing"
5+
6+
avsproto "github.com/AvaProtocol/EigenLayer-AVS/protobuf"
7+
"github.com/AvaProtocol/EigenLayer-AVS/core/config"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/oklog/ulid/v2"
10+
)
11+
12+
func TestRunNodeWithInputs_BlockTrigger(t *testing.T) {
13+
vm, err := NewVMWithData(nil, nil, &config.SmartWalletConfig{}, nil)
14+
assert.NoError(t, err)
15+
16+
node, err := CreateNodeFromType("blockTrigger", map[string]interface{}{
17+
"blockNumber": float64(12345),
18+
}, "")
19+
assert.NoError(t, err)
20+
21+
result, err := vm.RunNodeWithInputs(node, map[string]interface{}{})
22+
23+
assert.NoError(t, err)
24+
assert.NotNil(t, result)
25+
assert.True(t, result.Success)
26+
27+
codeOutput := result.GetCustomCode()
28+
assert.NotNil(t, codeOutput)
29+
assert.NotNil(t, codeOutput.Data)
30+
}
31+
32+
func TestRunNodeWithInputs_CustomCode(t *testing.T) {
33+
vm, err := NewVMWithData(nil, nil, &config.SmartWalletConfig{}, nil)
34+
assert.NoError(t, err)
35+
36+
nodeId := "test_" + ulid.Make().String()
37+
node := &avsproto.TaskNode{
38+
Id: nodeId,
39+
Name: "Test Custom Code",
40+
TaskType: &avsproto.TaskNode_CustomCode{
41+
CustomCode: &avsproto.CustomCodeNode{
42+
Lang: avsproto.CustomCodeLang_JavaScript,
43+
Source: `
44+
if (typeof myVar === 'undefined') {
45+
throw new Error("myVar is required but not provided");
46+
}
47+
return { result: myVar * 2 };
48+
`,
49+
},
50+
},
51+
}
52+
53+
_, err = vm.RunNodeWithInputs(node, map[string]interface{}{})
54+
assert.Error(t, err)
55+
56+
result, err := vm.RunNodeWithInputs(node, map[string]interface{}{
57+
"myVar": 5,
58+
})
59+
assert.NoError(t, err)
60+
assert.NotNil(t, result)
61+
assert.True(t, result.Success)
62+
63+
codeOutput := result.GetCustomCode()
64+
assert.NotNil(t, codeOutput)
65+
assert.NotNil(t, codeOutput.Data)
66+
}
67+
68+
func TestCreateNodeFromType(t *testing.T) {
69+
nodeTypes := []string{"blockTrigger", "restApi", "contractRead", "customCode", "branch", "filter"}
70+
71+
for _, nodeType := range nodeTypes {
72+
config := map[string]interface{}{}
73+
74+
switch nodeType {
75+
case "blockTrigger":
76+
config["blockNumber"] = float64(12345)
77+
case "restApi":
78+
config["url"] = "https://example.com"
79+
config["method"] = "GET"
80+
case "contractRead":
81+
config["contractAddress"] = "0x1234567890123456789012345678901234567890"
82+
config["callData"] = "0x12345678"
83+
case "customCode":
84+
config["source"] = "return { hello: 'world' };"
85+
case "branch":
86+
config["conditions"] = []interface{}{
87+
map[string]interface{}{
88+
"id": "cond1",
89+
"type": "if",
90+
"expression": "true",
91+
},
92+
}
93+
case "filter":
94+
config["expression"] = "item > 5"
95+
config["input"] = "inputArray"
96+
}
97+
98+
node, err := CreateNodeFromType(nodeType, config, "")
99+
assert.NoError(t, err)
100+
assert.NotNil(t, node)
101+
assert.NotEmpty(t, node.Id)
102+
assert.Equal(t, "Single Node Execution", node.Name)
103+
104+
switch nodeType {
105+
case "blockTrigger":
106+
assert.NotNil(t, node.GetCustomCode())
107+
case "restApi":
108+
assert.NotNil(t, node.GetRestApi())
109+
case "contractRead":
110+
assert.NotNil(t, node.GetContractRead())
111+
case "customCode":
112+
assert.NotNil(t, node.GetCustomCode())
113+
case "branch":
114+
assert.NotNil(t, node.GetBranch())
115+
case "filter":
116+
assert.NotNil(t, node.GetFilter())
117+
}
118+
}
119+
}
120+
121+
func TestEngine_RunNodeWithInputs(t *testing.T) {
122+
engine := New(nil, &config.Config{
123+
SmartWallet: &config.SmartWalletConfig{},
124+
}, nil, nil)
125+
126+
result, err := engine.RunNodeWithInputs("blockTrigger", map[string]interface{}{
127+
"blockNumber": float64(12345),
128+
}, map[string]interface{}{})
129+
130+
if err == nil {
131+
assert.NotNil(t, result)
132+
assert.Contains(t, result, "blockNumber")
133+
assert.Equal(t, float64(12345), result["blockNumber"])
134+
}
135+
136+
result, err = engine.RunNodeWithInputs("customCode", map[string]interface{}{
137+
"source": "return { message: 'Hello, World!' };",
138+
}, map[string]interface{}{})
139+
140+
if err == nil {
141+
assert.NotNil(t, result)
142+
assert.Contains(t, result, "message")
143+
assert.Equal(t, "Hello, World!", result["message"])
144+
}
145+
}

0 commit comments

Comments
 (0)