github.com/blend/go-sdk@v1.20220411.3/vault/decompose_json.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package vault 9 10 import ( 11 "encoding/json" 12 "reflect" 13 "strings" 14 15 "github.com/blend/go-sdk/ex" 16 "github.com/blend/go-sdk/reflectutil" 17 ) 18 19 // Constants 20 const ( 21 StructTag = "secret" 22 ) 23 24 // IsZeroable is useful to test if we need to set a config field or not. 25 type IsZeroable interface { 26 IsZero() bool 27 } 28 29 // DecomposeJSON decomposes an object into json fields marked with the `secret` struct tag. 30 // Top level fields will get their own keys. 31 // Nested objects are serialized as json. 32 func DecomposeJSON(obj interface{}) (map[string]string, error) { 33 output := make(map[string]string) 34 35 objType := reflectutil.Type(obj) 36 objValue := reflectutil.Value(obj) 37 38 var field reflect.StructField 39 var fieldValue reflect.Value 40 var fieldValueValue interface{} 41 var tagValue string 42 var outputKey string 43 var tagPieces []string 44 var typed IsZeroable 45 var ok bool 46 for x := 0; x < objType.NumField(); x++ { 47 field = objType.Field(x) 48 if !reflectutil.IsExported(field.Name) { 49 continue 50 } 51 52 fieldValue = objValue.FieldByName(field.Name) 53 tagValue = field.Tag.Get(StructTag) 54 if tagValue == "" || tagValue == "-" { 55 continue 56 } 57 tagPieces = strings.Split(tagValue, ",") 58 outputKey = tagPieces[0] 59 60 fieldValueValue = fieldValue.Interface() 61 if typed, ok = fieldValueValue.(IsZeroable); ok && typed.IsZero() { 62 continue 63 } 64 65 // skip empty values 66 if reflectutil.IsEmptyValue(fieldValue) { 67 continue 68 } 69 70 contents, err := json.Marshal(fieldValueValue) 71 if err != nil { 72 return nil, ex.New(err) 73 } 74 output[outputKey] = string(contents) 75 } 76 77 return output, nil 78 } 79 80 // RestoreJSON restores an object from a given data bag as JSON. 81 func RestoreJSON(data map[string]string, obj interface{}) error { 82 objType := reflectutil.Type(obj) 83 objValue := reflectutil.Value(obj) 84 85 fieldLookup := make(map[string]string) 86 87 var field reflect.StructField 88 var fieldValue reflect.Value 89 var tagValue string 90 var outputKey string 91 var tagPieces []string 92 for x := 0; x < objType.NumField(); x++ { 93 field = objType.Field(x) 94 if !reflectutil.IsExported(field.Name) { 95 continue 96 } 97 98 fieldValue = objValue.FieldByName(field.Name) 99 tagValue = field.Tag.Get(StructTag) 100 if tagValue == "" || tagValue == "-" { 101 continue 102 } 103 tagPieces = strings.Split(tagValue, ",") 104 outputKey = tagPieces[0] 105 fieldLookup[outputKey] = field.Name 106 } 107 108 var fieldName string 109 var ok bool 110 for key, value := range data { 111 // figure out which field matches the key ... 112 if fieldName, ok = fieldLookup[key]; !ok { 113 continue 114 } 115 fieldValue = objValue.FieldByName(fieldName) 116 if err := json.Unmarshal([]byte(value), fieldValue.Addr().Interface()); err != nil { 117 return ex.New(err) 118 } 119 } 120 return nil 121 }