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 }