github.com/alkemics/goflow@v0.2.1/wrappers/bind/wrappers.go (about) 1 package bind 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/alkemics/goflow" 8 ) 9 10 func Wrapper(unmarshal func(interface{}) error, graph goflow.GraphRenderer) (goflow.GraphRenderer, error) { 11 var binds struct { 12 Nodes []struct { 13 ID string 14 Bind map[string]bindings 15 } 16 } 17 if err := unmarshal(&binds); err != nil { 18 return nil, err 19 } 20 21 bindingsByID := make(map[string]map[string]bindings) 22 for _, node := range binds.Nodes { 23 bindingsByID[node.ID] = node.Bind 24 } 25 26 errs := make([]error, 0) 27 nodes := graph.Nodes() 28 wrappedNodes := make([]goflow.NodeRenderer, len(nodes)) 29 bindingNodes := make([]bindingNode, 0) 30 for i, node := range nodes { 31 bn := bindingsByID[node.ID()] 32 if bn == nil { 33 bn = make(map[string]bindings) 34 } 35 nodeInputs := node.Inputs() 36 37 unkownInputs := make([]string, 0) 38 bindMap := make(map[string]goflow.Field) 39 for input, bs := range bn { 40 var nodeInput goflow.Field 41 for _, in := range nodeInputs { 42 if in.Name == input { 43 nodeInput = in 44 break 45 } 46 } 47 48 if nodeInput.Name == "" { 49 unkownInputs = append(unkownInputs, input) 50 continue 51 } 52 53 aggregatorNodeID := fmt.Sprintf("__%s_%s", node.ID(), input) 54 inputs := make([]goflow.Field, len(bs)) 55 for i, b := range bs { 56 f := goflow.Field{ 57 Name: b, 58 Type: fmt.Sprintf("?%s", nodeInput.Type), 59 } 60 inputs[i] = f 61 } 62 bindingNodes = append(bindingNodes, bindingNode{ 63 id: aggregatorNodeID, 64 inputs: inputs, 65 output: goflow.Field{ 66 Name: "aggregated", 67 Type: nodeInput.Type, 68 }, 69 }) 70 bindMap[input] = goflow.Field{ 71 Name: fmt.Sprintf("%s.aggregated", aggregatorNodeID), 72 Type: nodeInput.Type, 73 } 74 } 75 76 if len(unkownInputs) > 0 { 77 errs = append(errs, goflow.NodeError{ 78 ID: node.ID(), 79 Wrapper: "bind", 80 Err: fmt.Errorf("unknown inputs: [%s]", strings.Join(unkownInputs, ", ")), 81 }) 82 } 83 84 // Check that all inputs have been given a value. 85 missingInputNames := make([]string, 0) 86 optionalInputNames := getOptionalInputNames(node.Doc()) 87 absentInputNames := make(map[string]struct{}) 88 for _, input := range nodeInputs { 89 // Needed to allow other wrappers to inject inputs 90 // TODO: inject the decorators via inputs.decorators 91 if strings.Contains(input.Name, ".") || input.Name == "decorators" { 92 continue 93 } 94 95 if _, ok := bindMap[input.Name]; !ok { 96 if _, ok := optionalInputNames[input.Name]; ok { 97 absentInputNames[input.Name] = struct{}{} 98 } else { 99 missingInputNames = append(missingInputNames, input.Name) 100 } 101 } 102 } 103 if len(missingInputNames) > 0 { 104 errs = append(errs, goflow.NodeError{ 105 ID: node.ID(), 106 Wrapper: "bind", 107 Err: fmt.Errorf("inputs not bound: [%s]", strings.Join(missingInputNames, ", ")), 108 }) 109 continue 110 } 111 112 wrappedNodes[i] = nodeRenderer{ 113 NodeRenderer: node, 114 bindMap: bindMap, 115 absentInputNames: absentInputNames, 116 } 117 } 118 119 if len(errs) > 0 { 120 return nil, goflow.MultiError{Errs: errs} 121 } 122 123 return graphRenderer{ 124 GraphRenderer: graph, 125 wrappedNodes: wrappedNodes, 126 bindingNodes: bindingNodes, 127 }, nil 128 }