github.com/xmidt-org/webpa-common@v1.11.9/event/multimap.go (about)

     1  package event
     2  
     3  import "fmt"
     4  
     5  // MultiMap describes a set of events together with how those events should be processed.  Most often,
     6  // event types are mapped to URLs, but that is not required.  Event values can be any string that is
     7  // meaningful to an application.
     8  type MultiMap map[string][]string
     9  
    10  // Add appends one or more values to an event type.  If mappedTo is empty, this method does nothing.
    11  // If the eventType doesn't exist, it is created.
    12  func (m MultiMap) Add(eventType string, mappedTo ...string) {
    13  	if len(mappedTo) == 0 {
    14  		return
    15  	}
    16  
    17  	m[eventType] = append(m[eventType], mappedTo...)
    18  }
    19  
    20  // Set changes the given eventType so that it maps only to the values supplied in mappedTo.  If
    21  // mappedTo is empty, this method deletes the event type.
    22  func (m MultiMap) Set(eventType string, mappedTo ...string) {
    23  	if len(mappedTo) == 0 {
    24  		delete(m, eventType)
    25  		return
    26  	}
    27  
    28  	copyOf := make([]string, len(mappedTo))
    29  	copy(copyOf, mappedTo)
    30  	m[eventType] = copyOf
    31  }
    32  
    33  // Get returns the values associated with the given event type.  The fallback event types, if supplied, are used
    34  // if no values are present for the given eventType.  The fallback is useful for defaults, e.g. m.Get("IOT", "default").
    35  func (m MultiMap) Get(eventType string, fallback ...string) ([]string, bool) {
    36  	values, ok := m[eventType]
    37  	if !ok {
    38  		for i := 0; i < len(fallback) && !ok; i++ {
    39  			values, ok = m[fallback[i]]
    40  		}
    41  	}
    42  
    43  	return values, ok
    44  }
    45  
    46  // NestedToMultiMap translates a map with potentially nested string keys into a MultiMap.  This function is useful
    47  // when unmarshalling from libraries that impose some meaning on a separator, like viper does with periods.  Essentially,
    48  // this function returns a MultiMap that is the result of "flattening" the given raw map.
    49  //
    50  // The separator string must be nonempty.  It is used as the separator for nested map keys, e.g. "foo.bar".
    51  func NestedToMultiMap(separator string, raw map[string]interface{}) (MultiMap, error) {
    52  	if len(separator) == 0 {
    53  		return nil, fmt.Errorf("The separator cannot be empty")
    54  	}
    55  
    56  	output := make(MultiMap, len(raw))
    57  	if err := nestedToMultiMap("", separator, raw, output); err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	return output, nil
    62  }
    63  
    64  // nestedToMultiMap is a recursive function that builds a MultiMap by travsersing any nested maps with the raw map.
    65  func nestedToMultiMap(base, separator string, raw map[string]interface{}, output MultiMap) error {
    66  	var eventType string
    67  	for k, v := range raw {
    68  		if len(base) > 0 {
    69  			eventType = base + separator + k
    70  		} else {
    71  			eventType = k
    72  		}
    73  
    74  		switch value := v.(type) {
    75  		case string:
    76  			output.Set(eventType, value)
    77  
    78  		case []string:
    79  			output.Set(eventType, value...)
    80  
    81  		case []interface{}:
    82  			for _, rawElement := range value {
    83  				if stringElement, ok := rawElement.(string); ok {
    84  					output.Add(eventType, stringElement)
    85  				} else {
    86  					return fmt.Errorf("Invalid element value of type %T: %v", v, v)
    87  				}
    88  			}
    89  
    90  		case map[string]interface{}:
    91  			if err := nestedToMultiMap(eventType, separator, value, output); err != nil {
    92  				return err
    93  			}
    94  
    95  		case map[string][]string:
    96  			for nestedKey, nestedValues := range value {
    97  				output.Set(eventType+separator+nestedKey, nestedValues...)
    98  			}
    99  
   100  		default:
   101  			return fmt.Errorf("Invalid raw event value of type %T: %v", v, v)
   102  		}
   103  	}
   104  
   105  	return nil
   106  }