工作流

This commit is contained in:
PandaGoAdmin
2023-03-31 11:39:11 +08:00
parent b247985bbe
commit f7f9e67c95
16 changed files with 951 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
package nodes
import (
"github.com/sirupsen/logrus"
"pandax/pkg/flow_engine/message"
)
type externalDingNode struct {
bareNode
WebHook string `json:"webHook" yaml:"webHook"`
MsgType string `json:"msgType" yaml:"msgType"`
Content string `json:"content" yaml:"content"`
IsAtAll bool `json:"isAtAll" yaml:"isAtAll"`
atMobiles []string `json:"atMobiles" yaml:"atMobiles"`
}
type externalDingNodeFactory struct{}
func (f externalDingNodeFactory) Name() string { return "ExternalDingNode" }
func (f externalDingNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL }
func (f externalDingNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
func (f externalDingNodeFactory) Create(id string, meta Metadata) (Node, error) {
node := &externalDingNode{
bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
}
return decodePath(meta, node)
}
func (n *externalDingNode) Handle(msg message.Message) error {
logrus.Infof("%s handle message '%s'", n.Name(), msg.GetType())
return nil
}

View File

@@ -0,0 +1,51 @@
package nodes
import (
"github.com/sirupsen/logrus"
"pandax/pkg/flow_engine/message"
)
type externalSendEmailNode struct {
bareNode
From string `json:"from" yaml:"from"`
To string `json:"to" yaml:"to"`
Cc string `json:"cc" yaml:"cc"`
Bcc string `json:"bcc" yaml:"bcc"`
Subject string `json:"subject" yaml:"subject"`
Body string `json:"body" yaml:"body"`
}
type externalSendEmailNodeFactory struct{}
func (f externalSendEmailNodeFactory) Name() string { return "ExternalSendEmailNode" }
func (f externalSendEmailNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL }
func (f externalSendEmailNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
func (f externalSendEmailNodeFactory) Create(id string, meta Metadata) (Node, error) {
labels := []string{"Success", "Failure"}
node := &externalSendEmailNode{
bareNode: newBareNode(f.Name(), id, meta, labels),
}
return decodePath(meta, node)
}
func (n *externalSendEmailNode) Handle(msg message.Message) error {
logrus.Infof("%s handle message '%s'", n.Name(), msg.GetType())
successLabelNode := n.GetLinkedNode("Success")
//failureLabelNode := n.GetLinkedNode("Failure")
/*dialer := runtime.NewDialer(runtime.EMAIL)
variables := map[string]string{
"from": n.From,
"to": n.To,
"cc": n.Cc,
"bcc": n.Bcc,
"subject": n.Subject,
"body": n.Body,
}
if err := dialer.DialAndSend(msg.GetMetadata(), variables); err != nil {
return failureLabelNode.Handle(msg)
}*/
return successLabelNode.Handle(msg)
}

View File

@@ -0,0 +1,31 @@
package nodes
import (
"github.com/sirupsen/logrus"
"pandax/pkg/flow_engine/message"
)
type externalSendSmsNode struct {
bareNode
}
type externalSendSmsNodeFactory struct{}
func (f externalSendSmsNodeFactory) Name() string { return "ExternalSendSmslNode" }
func (f externalSendSmsNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL }
func (f externalSendSmsNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
func (f externalSendSmsNodeFactory) Create(id string, meta Metadata) (Node, error) {
node := &externalSendSmsNode{
bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
}
return decodePath(meta, node)
}
func (n *externalSendSmsNode) Handle(msg message.Message) error {
logrus.Infof("%s handle message '%s'", n.Name(), msg.GetType())
successLabelNode := n.GetLinkedNode("Success")
//failureLabelNode := n.GetLinkedNode("Failure")
return successLabelNode.Handle(msg)
}

View File

@@ -0,0 +1,33 @@
package nodes
import (
"github.com/sirupsen/logrus"
"pandax/pkg/flow_engine/message"
)
type externalWechatNode struct {
bareNode
WebHook string `json:"webHook" yaml:"webHook"`
MsgType string `json:"msgType" yaml:"msgType"`
Content string `json:"content" yaml:"content"`
IsAtAll bool `json:"isAtAll" yaml:"isAtAll"`
atMobiles []string `json:"atMobiles" yaml:"atMobiles"`
}
type externalWechatNodeFactory struct{}
func (f externalWechatNodeFactory) Name() string { return "ExternalWechatNode" }
func (f externalWechatNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL }
func (f externalWechatNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
func (f externalWechatNodeFactory) Create(id string, meta Metadata) (Node, error) {
node := &externalWechatNode{
bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
}
return decodePath(meta, node)
}
func (n *externalWechatNode) Handle(msg message.Message) error {
logrus.Infof("%s handle message '%s'", n.Name(), msg.GetType())
return nil
}

View File

@@ -0,0 +1,51 @@
package nodes
import (
"fmt"
)
const (
NODE_CATEGORY_TRANSFORM = "transform"
NODE_CATEGORY_EXTERNAL = "external"
NODE_CATEGORY_OTHERS = "others"
)
// Factory is node's factory to create node based on metadata
// factory also manage node's metadta description which can be used by other
// service to present node in web
type Factory interface {
Name() string
Category() string
Labels() []string
Create(id string, meta Metadata) (Node, error)
}
var (
// allNodeFactories hold all node's factory
allNodeFactories map[string]Factory = make(map[string]Factory)
// allNodeCategories hold node's metadata by category
allNodeCategories map[string][]map[string]interface{} = make(map[string][]map[string]interface{})
)
// RegisterFactory add a new node factory and classify its category for
// metadata description
func RegisterFactory(f Factory) {
allNodeFactories[f.Name()] = f
if allNodeCategories[f.Category()] == nil {
allNodeCategories[f.Category()] = []map[string]interface{}{}
}
allNodeCategories[f.Category()] = append(allNodeCategories[f.Category()], map[string]interface{}{"name": f.Name(), "labels": f.Labels()})
}
// NewNode is the only way to create a new node
func NewNode(nodeType string, id string, meta Metadata) (Node, error) {
if f, found := allNodeFactories[nodeType]; found {
return f.Create(id, meta)
}
return nil, fmt.Errorf("invalid node type '%s'", nodeType)
}
// GetCategoryNodes return specified category's all nodes
func GetCategoryNodes() map[string][]map[string]interface{} { return allNodeCategories }

View File

@@ -0,0 +1,13 @@
package nodes
// init register all node's factory
func init() {
RegisterFactory(inputNodeFactory{})
RegisterFactory(transformScriptNodeFactory{})
RegisterFactory(externalDingNodeFactory{})
RegisterFactory(externalWechatNodeFactory{})
RegisterFactory(externalSendEmailNodeFactory{})
RegisterFactory(externalSendSmsNodeFactory{})
}

View File

@@ -0,0 +1,34 @@
package nodes
import (
"github.com/sirupsen/logrus"
"pandax/pkg/flow_engine/message"
)
const InputNodeName = "InputNode"
type inputNode struct {
bareNode
}
type inputNodeFactory struct{}
func (f inputNodeFactory) Name() string { return "InputNode" }
func (f inputNodeFactory) Category() string { return NODE_CATEGORY_OTHERS }
func (f inputNodeFactory) Labels() []string { return []string{} }
func (f inputNodeFactory) Create(id string, meta Metadata) (Node, error) {
node := &inputNode{
bareNode: newBareNode(InputNodeName, id, meta, f.Labels()),
}
return node, nil
}
func (n *inputNode) Handle(msg message.Message) error {
logrus.Infof("%s handle message '%s'", n.Name(), msg.GetType())
nodes := n.GetLinkedNodes()
for _, node := range nodes {
return node.Handle(msg)
}
return nil
}

View File

@@ -0,0 +1,63 @@
package nodes
import (
"fmt"
"github.com/mitchellh/mapstructure"
)
const (
NODE_CONFIG_MESSAGE_TYPE_KEY = "messageTypeKey"
NODE_CONFIG_ORIGINATOR_TYPE_KEY = "originatorTypeKey"
)
type Metadata interface {
Keys() []string
With(key string, val interface{}) Metadata
Value(key string) (interface{}, error)
DecodePath(rawVal interface{}) error
}
type nodeMetadata struct {
keypairs map[string]interface{}
}
func NewMetadata() Metadata {
return &nodeMetadata{
keypairs: make(map[string]interface{}),
}
}
func NewMetadataWithString(vals string) Metadata {
return &nodeMetadata{}
}
func NewMetadataWithValues(vals map[string]interface{}) Metadata {
return &nodeMetadata{
keypairs: vals,
}
}
func (c *nodeMetadata) Keys() []string {
keys := []string{}
for key, _ := range c.keypairs {
keys = append(keys, key)
}
return keys
}
func (c *nodeMetadata) Value(key string) (interface{}, error) {
if val, found := c.keypairs[key]; found {
return val, nil
}
return nil, fmt.Errorf("key '%s' not found", key)
}
func (c *nodeMetadata) With(key string, val interface{}) Metadata {
c.keypairs[key] = val
return c
}
func (c *nodeMetadata) DecodePath(rawVal interface{}) error {
//return utils.Map2Struct(c.keypairs, rawVal)
return mapstructure.Decode(c.keypairs, rawVal)
}

View File

@@ -0,0 +1,65 @@
package nodes
import (
"errors"
"pandax/pkg/flow_engine/message"
"github.com/sirupsen/logrus"
)
type Node interface {
Name() string
Id() string
Metadata() Metadata
MustLabels() []string
Handle(message.Message) error
AddLinkedNode(label string, node Node)
GetLinkedNode(label string) Node
GetLinkedNodes() map[string]Node
}
type bareNode struct {
name string
id string
nodes map[string]Node
meta Metadata
labels []string
}
func newBareNode(name string, id string, meta Metadata, labels []string) bareNode {
return bareNode{
name: name,
id: id,
nodes: make(map[string]Node),
meta: meta,
labels: labels,
}
}
func (n *bareNode) Name() string { return n.name }
func (n *bareNode) WithId(id string) { n.id = id }
func (n *bareNode) Id() string { return n.id }
func (n *bareNode) MustLabels() []string { return n.labels }
func (n *bareNode) AddLinkedNode(label string, node Node) { n.nodes[label] = node }
func (n *bareNode) GetLinkedNode(label string) Node {
if node, found := n.nodes[label]; found {
return node
}
logrus.Errorf("no label '%s' in node '%s'", label, n.name)
return nil
}
func (n *bareNode) GetLinkedNodes() map[string]Node { return n.nodes }
func (n *bareNode) Metadata() Metadata { return n.meta }
func (n *bareNode) Handle(message.Message) error { return errors.New("not implemented") }
func decodePath(meta Metadata, n Node) (Node, error) {
if err := meta.DecodePath(n); err != nil {
return n, err
}
return n, nil
}

View File

@@ -0,0 +1,84 @@
package nodes
import (
"github.com/dop251/goja"
"github.com/sirupsen/logrus"
"pandax/pkg/flow_engine/message"
)
type ScriptEngine interface {
ScriptOnMessage(msg message.Message, script string) (message.Message, error)
//used by filter_switch_node
ScriptOnSwitch(msg message.Message, script string) ([]string, error)
//used by filter_script_node
ScriptOnFilter(msg message.Message, script string) (bool, error)
ScriptToString(msg message.Message, script string) (string, error)
}
type baseScriptEngine struct {
}
func NewScriptEngine() ScriptEngine {
return &baseScriptEngine{}
}
func (bse *baseScriptEngine) ScriptOnMessage(msg message.Message, script string) (message.Message, error) {
vm := goja.New()
_, err := vm.RunString(script)
if err != nil {
logrus.Info("JS代码有问题")
return nil, err
}
var fn func(map[string]interface{}, map[string]interface{}, string) map[string]interface{}
err = vm.ExportTo(vm.Get("Transform"), &fn)
if err != nil {
logrus.Info("Js函数映射到 Go 函数失败!")
return nil, err
}
datas := fn(msg.GetMsg(), msg.GetMetadata().GetValues(), msg.GetType())
msg.SetMsg(datas["msg"].(map[string]interface{}))
msg.SetMetadata(message.NewDefaultMetadata(datas["metadata"].(map[string]interface{})))
msg.SetType(datas["msgType"].(string))
return msg, nil
return nil, nil
}
func (bse *baseScriptEngine) ScriptOnSwitch(msg message.Message, script string) ([]string, error) {
vm := goja.New()
_, err := vm.RunString(script)
if err != nil {
logrus.Info("JS代码有问题")
return nil, err
}
var fn func(map[string]interface{}, map[string]interface{}, string) []string
err = vm.ExportTo(vm.Get("Switch"), &fn)
if err != nil {
logrus.Info("Js函数映射到 Go 函数失败!")
return nil, err
}
datas := fn(msg.GetMsg(), msg.GetMetadata().GetValues(), msg.GetType())
return datas, nil
}
func (bse *baseScriptEngine) ScriptOnFilter(msg message.Message, script string) (bool, error) {
vm := goja.New()
_, err := vm.RunString(script)
if err != nil {
logrus.Info("JS代码有问题")
return false, err
}
var fn func(map[string]interface{}, map[string]interface{}, string) bool
err = vm.ExportTo(vm.Get("Filter"), &fn)
if err != nil {
logrus.Info("Js函数映射到 Go 函数失败!")
return false, err
}
datas := fn(msg.GetMsg(), msg.GetMetadata().GetValues(), msg.GetType())
return datas, nil
}
func (bse *baseScriptEngine) ScriptToString(msg message.Message, script string) (string, error) {
return "", nil
}

View File

@@ -0,0 +1,38 @@
package nodes
import (
"github.com/sirupsen/logrus"
"pandax/pkg/flow_engine/message"
)
type transformScriptNode struct {
bareNode
Script string `json:"script" yaml:"script"`
}
type transformScriptNodeFactory struct{}
func (f transformScriptNodeFactory) Name() string { return "TransformScriptNode" }
func (f transformScriptNodeFactory) Category() string { return NODE_CATEGORY_TRANSFORM }
func (f transformScriptNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
func (f transformScriptNodeFactory) Create(id string, meta Metadata) (Node, error) {
labels := []string{"Success", "Failure"}
node := &transformScriptNode{
bareNode: newBareNode(f.Name(), id, meta, labels),
}
return decodePath(meta, node)
}
func (n *transformScriptNode) Handle(msg message.Message) error {
logrus.Infof("%s handle message '%s'", n.Name(), msg.GetType())
successLabelNode := n.GetLinkedNode("Success")
failureLabelNode := n.GetLinkedNode("Failure")
scriptEngine := NewScriptEngine()
newMessage, err := scriptEngine.ScriptOnMessage(msg, n.Script)
if err != nil {
return failureLabelNode.Handle(msg)
}
return successLabelNode.Handle(newMessage)
}