github.com/Jeffail/benthos/v3@v3.65.0/lib/output/broker_out_common.go (about)

     1  package output
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/Jeffail/benthos/v3/lib/broker"
    10  
    11  	"gopkg.in/yaml.v3"
    12  )
    13  
    14  //------------------------------------------------------------------------------
    15  
    16  type brokerOutputList []Config
    17  
    18  // UnmarshalJSON ensures that when parsing configs that are in a map or slice
    19  // the default values are still applied.
    20  func (b *brokerOutputList) UnmarshalJSON(bytes []byte) error {
    21  	genericOutputs := []interface{}{}
    22  	if err := json.Unmarshal(bytes, &genericOutputs); err != nil {
    23  		return err
    24  	}
    25  
    26  	outputConfs, err := parseOutputConfsWithDefaults(genericOutputs)
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	*b = outputConfs
    32  	return nil
    33  }
    34  
    35  // UnmarshalYAML ensures that when parsing configs that are in a map or slice
    36  // the default values are still applied.
    37  func (b *brokerOutputList) UnmarshalYAML(unmarshal func(interface{}) error) error {
    38  	genericOutputs := []interface{}{}
    39  	if err := unmarshal(&genericOutputs); err != nil {
    40  		return err
    41  	}
    42  
    43  	outputConfs, err := parseOutputConfsWithDefaults(genericOutputs)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	*b = outputConfs
    49  	return nil
    50  }
    51  
    52  //------------------------------------------------------------------------------
    53  
    54  // parseOutputConfsWithDefaults takes an array of output configs as
    55  // []interface{} and returns an array of output configs with default values in
    56  // place of omitted values. This is necessary because when unmarshalling config
    57  // files using structs you can pre-populate non-reference type struct fields
    58  // with default values, but array objects will lose those defaults.
    59  //
    60  // In order to ensure that omitted values are set to default we initially parse
    61  // the array as interface{} types and then individually apply the defaults by
    62  // marshalling and unmarshalling. The correct way to do this would be to use
    63  // json.RawMessage, but our config files can contain a range of different
    64  // formats that we do not know at this stage (JSON, YAML, etc), therefore we use
    65  // the more hacky method as performance is not an issue at this stage.
    66  func parseOutputConfsWithDefaults(outConfs []interface{}) ([]Config, error) {
    67  	outputConfs := []Config{}
    68  
    69  	for i, boxedConfig := range outConfs {
    70  		newConfs := make([]Config, 1)
    71  		label := broker.GetGenericType(boxedConfig)
    72  
    73  		if i > 0 && strings.Index(label, "ditto") == 0 {
    74  			broker.RemoveGenericType(boxedConfig)
    75  
    76  			// Check if there is a ditto multiplier.
    77  			if len(label) > 5 && label[5] == '_' {
    78  				if label[6:] == "0" {
    79  					// This is a special case where we are expressing that
    80  					// we want to end up with zero duplicates.
    81  					newConfs = nil
    82  				} else {
    83  					n, err := strconv.Atoi(label[6:])
    84  					if err != nil {
    85  						return nil, fmt.Errorf("failed to parse ditto multiplier: %v", err)
    86  					}
    87  					newConfs = make([]Config, n)
    88  				}
    89  			} else {
    90  				newConfs = make([]Config, 1)
    91  			}
    92  
    93  			broker.ComplementGenericConfig(boxedConfig, outConfs[i-1])
    94  		}
    95  
    96  		for _, conf := range newConfs {
    97  			rawBytes, err := yaml.Marshal(boxedConfig)
    98  			if err != nil {
    99  				return nil, err
   100  			}
   101  			if err := yaml.Unmarshal(rawBytes, &conf); err != nil {
   102  				return nil, err
   103  			}
   104  			outputConfs = append(outputConfs, conf)
   105  		}
   106  	}
   107  
   108  	return outputConfs, nil
   109  }
   110  
   111  //------------------------------------------------------------------------------