fixed apiconfig

This commit is contained in:
2025-07-09 01:46:58 +01:00
parent 451d408c24
commit ef980f0cb5
6 changed files with 659 additions and 149 deletions
+8 -3
View File
@@ -11,14 +11,18 @@ const (
type APIConfig struct {
ID string `json:"id" yaml:"id"`
ListenPath string `json:"listenPath" yaml:"listenPath"`
Method string `json:"method" yaml:"method"`
Request RequestConfig `json:"request" yaml:"request"`
Actions map[string]Action `json:"actions" yaml:"actions"`
Conditionals map[string]Conditional `json:"conditionals" yaml:"conditionals"`
Responses map[string]ResponseConfig `json:"responses" yaml:"responses"`
HttpConfig HttpConfig `json:"http" yaml:"http"`
McpTool MCPToolConfig `json:"mcpTool" yaml:"mcpTool"`
// Deprecated: use HttpConfig.Listenpath
ListenPath string `json:"listenPath" yaml:"listenPath"`
// Deprecated: use HttpConfig.Method
Method string `json:"method" yaml:"method"`
// Deprecated: use HttpConfig
Request RequestConfig `json:"request" yaml:"request"`
}
type HttpConfig struct {
@@ -33,6 +37,7 @@ type McpConfig struct {
}
type MCPToolConfig struct {
Enabled bool `json:"enabled" yaml:"enabled"`
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`
Args map[string]ArgType `json:"args" yaml:"args"`
+92
View File
@@ -0,0 +1,92 @@
package apiconfig
import "git.servflow.io/servflow/definitions/proto"
func ProtoConfigToAPIConfig(configs []*proto.WorkflowConfig) []*APIConfig {
cfgs := make([]*APIConfig, 0)
for i := range configs {
protoConfig := configs[i]
if protoConfig == nil || protoConfig.Id == "" || protoConfig.Status == proto.Status_INACTIVE {
continue
}
a := APIConfig{
ID: protoConfig.Id,
ListenPath: protoConfig.ListenPath,
Method: protoConfig.Method,
Request: RequestConfig{
Next: protoConfig.Request.Next,
},
Actions: map[string]Action{},
Conditionals: map[string]Conditional{},
Responses: map[string]ResponseConfig{},
McpTool: MCPToolConfig{
Enabled: protoConfig.Mcp.Enabled,
Name: protoConfig.Mcp.Name,
Result: protoConfig.Mcp.Result,
Description: protoConfig.Mcp.Description,
Start: protoConfig.Mcp.Start,
Args: make(map[string]ArgType),
},
HttpConfig: HttpConfig{
Next: protoConfig.Request.Next,
ListenPath: protoConfig.Request.ListenPath,
Method: protoConfig.Request.Method,
},
}
for k := range protoConfig.Mcp.Args {
a.McpTool.Args[k] = ArgType{
Name: protoConfig.Mcp.Args[k].Name,
Type: protoConfig.Mcp.Args[k].Type,
}
}
for key := range protoConfig.Actions {
act := protoConfig.Actions[key]
a.Actions[key] = Action{
Type: act.Type,
Config: act.Config,
Next: act.Next,
Fail: act.Fail,
}
}
for key := range protoConfig.Conditionals {
cond := protoConfig.Conditionals[key]
a.Conditionals[key] = Conditional{
ValidPath: cond.ValidPath,
InvalidPath: cond.InvalidPath,
Expression: cond.Expression,
}
}
for key := range protoConfig.Responses {
resp := protoConfig.Responses[key]
a.Responses[key] = ResponseConfig{
Code: int(resp.GetCode()),
Type: resp.Type,
Template: string(resp.Template),
}
}
a.Normalize()
cfgs = append(cfgs, &a)
}
return cfgs
}
func (a *APIConfig) Normalize() {
if a.HttpConfig.Method == "" {
a.HttpConfig.Method = a.Method
}
if a.HttpConfig.Next == "" {
a.HttpConfig.Next = a.Request.Next
}
if a.HttpConfig.ListenPath == "" {
a.HttpConfig.ListenPath = a.ListenPath
}
if len(a.HttpConfig.CORSAllowedOrigins) < 1 {
a.HttpConfig.CORSAllowedOrigins = a.Request.CORSAllowedOrigins
}
}
+169
View File
@@ -0,0 +1,169 @@
package apiconfig
import (
"encoding/json"
"git.servflow.io/servflow/definitions/proto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestProtoConfigToAPIConfig(t *testing.T) {
tests := []struct {
name string
input []*proto.WorkflowConfig
validate func(t *testing.T, result []*APIConfig)
}{
{
name: "empty input slice",
input: []*proto.WorkflowConfig{},
validate: func(t *testing.T, result []*APIConfig) {
assert.Empty(t, result)
},
},
{
name: "nil config should be skipped",
input: []*proto.WorkflowConfig{
nil,
},
validate: func(t *testing.T, result []*APIConfig) {
assert.Empty(t, result)
},
},
{
name: "inactive config should be skipped",
input: []*proto.WorkflowConfig{
{
Id: "test-id",
Status: proto.Status_INACTIVE,
},
},
validate: func(t *testing.T, result []*APIConfig) {
assert.Empty(t, result)
},
},
{
name: "empty ID should be skipped",
input: []*proto.WorkflowConfig{
{
Status: proto.Status_ACTIVE,
},
},
validate: func(t *testing.T, result []*APIConfig) {
assert.Empty(t, result)
},
},
{
name: "valid config should be converted",
input: []*proto.WorkflowConfig{
{
Id: "test-id",
Status: proto.Status_ACTIVE,
ListenPath: "/test",
Method: "POST",
Request: &proto.Request{
Next: "next-action",
ListenPath: "/test",
Method: "GET",
},
Actions: map[string]*proto.Action{
"action1": {
Type: "http",
Config: json.RawMessage(`{"url": "http://example.com"}`),
Next: "action2",
Fail: "error",
},
},
Mcp: &proto.MCPConfig{
Enabled: true,
Name: "test-tool",
Args: map[string]*proto.MCPArgConfig{
"arg1": {
Name: "arg1",
Type: "string",
},
},
Description: "test description",
Start: "start",
Result: "result",
},
Conditionals: map[string]*proto.Conditional{
"cond1": {
ValidPath: "valid",
InvalidPath: "invalid",
Expression: "response.code == 200",
},
},
Responses: map[string]*proto.Response{
"success": {
Code: 200,
Type: "json",
Template: []byte(`{"status": "ok"}`),
},
},
},
},
validate: func(t *testing.T, result []*APIConfig) {
require.Len(t, result, 1)
cfg := result[0]
// Verify basic fields
assert.Equal(t, "test-id", cfg.ID)
assert.Equal(t, "/test", cfg.ListenPath)
assert.Equal(t, "POST", cfg.Method)
// Verify request config
assert.Equal(t, "next-action", cfg.Request.Next)
// Verify actions
require.Contains(t, cfg.Actions, "action1")
action := cfg.Actions["action1"]
assert.Equal(t, "http", action.Type)
assert.JSONEq(t, `{"url": "http://example.com"}`, string(action.Config))
assert.Equal(t, "action2", action.Next)
assert.Equal(t, "error", action.Fail)
// Verify conditionals
require.Contains(t, cfg.Conditionals, "cond1")
cond := cfg.Conditionals["cond1"]
assert.Equal(t, "valid", cond.ValidPath)
assert.Equal(t, "invalid", cond.InvalidPath)
assert.Equal(t, "response.code == 200", cond.Expression)
// Verify responses
require.Contains(t, cfg.Responses, "success")
resp := cfg.Responses["success"]
assert.Equal(t, 200, resp.Code)
assert.Equal(t, "json", resp.Type)
assert.Equal(t, `{"status": "ok"}`, resp.Template)
assert.Equal(t, MCPToolConfig{
Enabled: true,
Name: "test-tool",
Args: map[string]ArgType{
"arg1": {
Name: "arg1",
Type: "string",
},
},
Description: "test description",
Start: "start",
Result: "result",
}, cfg.McpTool)
assert.Equal(t, HttpConfig{
Next: "next-action",
ListenPath: "/test",
Method: "GET",
}, cfg.HttpConfig)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := ProtoConfigToAPIConfig(tt.input)
tt.validate(t, result)
})
}
}