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  }