github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/events/struct.go (about)

     1  /*
     2  Copyright 2021 Gravitational, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package events
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  
    23  	"github.com/gogo/protobuf/jsonpb"
    24  	"github.com/gogo/protobuf/types"
    25  	"github.com/gravitational/trace"
    26  )
    27  
    28  // Struct is a wrapper around types.Struct
    29  // that marshals itself into json
    30  type Struct struct {
    31  	types.Struct
    32  }
    33  
    34  // decodeToMap converts a pb.Struct to a map from strings to Go types.
    35  func decodeToMap(s *types.Struct) (map[string]interface{}, error) {
    36  	if s == nil {
    37  		return nil, nil
    38  	}
    39  	m := map[string]interface{}{}
    40  	for k, v := range s.Fields {
    41  		var err error
    42  		m[k], err = decodeValue(v)
    43  		if err != nil {
    44  			return nil, trace.Wrap(err)
    45  		}
    46  	}
    47  	return m, nil
    48  }
    49  
    50  // decodeValue decodes proto value to golang type
    51  func decodeValue(v *types.Value) (interface{}, error) {
    52  	switch k := v.Kind.(type) {
    53  	case *types.Value_NullValue:
    54  		return nil, nil
    55  	case *types.Value_NumberValue:
    56  		return k.NumberValue, nil
    57  	case *types.Value_StringValue:
    58  		return k.StringValue, nil
    59  	case *types.Value_BoolValue:
    60  		return k.BoolValue, nil
    61  	case *types.Value_StructValue:
    62  		return decodeToMap(k.StructValue)
    63  	case *types.Value_ListValue:
    64  		s := make([]interface{}, len(k.ListValue.Values))
    65  		for i, e := range k.ListValue.Values {
    66  			var err error
    67  			s[i], err = decodeValue(e)
    68  			if err != nil {
    69  				return nil, trace.Wrap(err)
    70  			}
    71  		}
    72  		return s, nil
    73  	default:
    74  		return nil, trace.BadParameter("protostruct: unknown kind %v", k)
    75  	}
    76  }
    77  
    78  // MarshalJSON marshals boolean value.
    79  func (s *Struct) MarshalJSON() ([]byte, error) {
    80  	m, err := decodeToMap(&s.Struct)
    81  	if err != nil {
    82  		return nil, trace.Wrap(err)
    83  	}
    84  	return json.Marshal(m)
    85  }
    86  
    87  // UnmarshalJSON unmarshals JSON from string or bool,
    88  // in case if value is missing or not recognized, defaults to false
    89  func (s *Struct) UnmarshalJSON(data []byte) error {
    90  	if len(data) == 0 {
    91  		return nil
    92  	}
    93  	err := jsonpb.Unmarshal(bytes.NewReader(data), &s.Struct)
    94  	if err != nil {
    95  		return trace.Wrap(err)
    96  	}
    97  	return nil
    98  }
    99  
   100  // EncodeMap encodes map[string]interface{} to map<string, Value>
   101  func EncodeMap(msg map[string]interface{}) (*Struct, error) {
   102  	data, err := json.Marshal(msg)
   103  	if err != nil {
   104  		return nil, trace.Wrap(err)
   105  	}
   106  	pbs := types.Struct{}
   107  	if err = jsonpb.Unmarshal(bytes.NewReader(data), &pbs); err != nil {
   108  		return nil, trace.Wrap(err)
   109  	}
   110  	return &Struct{Struct: pbs}, nil
   111  }
   112  
   113  // EncodeMapStrings encodes map[string][]string to map<string, Value>
   114  func EncodeMapStrings(msg map[string][]string) (*Struct, error) {
   115  	data, err := json.Marshal(msg)
   116  	if err != nil {
   117  		return nil, trace.Wrap(err)
   118  	}
   119  	pbs := types.Struct{}
   120  	if err = jsonpb.Unmarshal(bytes.NewReader(data), &pbs); err != nil {
   121  		return nil, trace.Wrap(err)
   122  	}
   123  	return &Struct{Struct: pbs}, nil
   124  }
   125  
   126  // MustEncodeMap panics if EncodeMap returns error
   127  func MustEncodeMap(msg map[string]interface{}) *Struct {
   128  	m, err := EncodeMap(msg)
   129  	if err != nil {
   130  		panic(err)
   131  	}
   132  	return m
   133  }