github.com/m3db/m3@v1.5.0/src/x/net/http/convert.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package xhttp 22 23 import ( 24 "encoding/json" 25 "errors" 26 "fmt" 27 "io" 28 "strconv" 29 "strings" 30 "time" 31 ) 32 33 const ( 34 durationSuffix = "Duration" 35 nanosSuffix = "Nanos" 36 ) 37 38 var ( 39 errDurationType = errors.New("invalid duration type") 40 ) 41 42 // NanosToDurationBytes transforms a json byte slice with Nano keys into Duration keys. 43 func NanosToDurationBytes(r io.Reader) (map[string]interface{}, error) { 44 var dict map[string]interface{} 45 d := json.NewDecoder(r) 46 d.UseNumber() 47 if err := d.Decode(&dict); err != nil { 48 return nil, fmt.Errorf("error decoding JSON: %s", err.Error()) 49 } 50 51 ret, err := NanosToDurationMap(dict) 52 if err != nil { 53 return nil, err 54 } 55 56 return ret, nil 57 } 58 59 // NanosToDurationMap transforms keys with Nanos into Duration. 60 func NanosToDurationMap(input map[string]interface{}) (map[string]interface{}, error) { 61 dictTranslated := make(map[string]interface{}, len(input)) 62 63 for k, v := range input { 64 if strings.HasSuffix(k, nanosSuffix) { 65 newKey := strings.Replace(k, nanosSuffix, durationSuffix, 1) 66 67 switch vv := v.(type) { 68 case string: 69 i, err := strconv.ParseInt(vv, 10, 64) 70 if err != nil { 71 return nil, err 72 } 73 74 dur := time.Duration(i) * time.Nanosecond 75 dictTranslated[newKey] = dur.String() 76 case json.Number: 77 // json.Number when using a json decoder with UseNumber() 78 // Assume given number is in nanos 79 vvNum, err := vv.Int64() 80 if err != nil { 81 return nil, err 82 } 83 84 dur := time.Duration(vvNum) * time.Nanosecond 85 dictTranslated[newKey] = dur.String() 86 case float64: 87 // float64 when unmarshaling without UseNumber() 88 // Assume given number is in nanos 89 dur := time.Duration(int64(vv)) * time.Nanosecond 90 dictTranslated[newKey] = dur.String() 91 default: 92 // Has Nano suffix, but is not string or number. 93 return nil, errDurationType 94 } 95 } else { 96 switch vv := v.(type) { 97 case map[string]interface{}: 98 durMap, err := NanosToDurationMap(vv) 99 if err != nil { 100 return nil, err 101 } 102 103 dictTranslated[k] = durMap 104 case []interface{}: 105 durArr, err := nanosToDurationArray(vv) 106 if err != nil { 107 return nil, err 108 } 109 110 dictTranslated[k] = durArr 111 default: 112 dictTranslated[k] = vv 113 } 114 } 115 } 116 117 return dictTranslated, nil 118 } 119 120 func nanosToDurationArray(input []interface{}) ([]interface{}, error) { 121 arrTranslated := make([]interface{}, len(input)) 122 for i, elem := range input { 123 switch v := elem.(type) { 124 case map[string]interface{}: 125 durMap, err := NanosToDurationMap(v) 126 if err != nil { 127 return nil, err 128 } 129 130 arrTranslated[i] = durMap 131 case []interface{}: 132 durArr, err := nanosToDurationArray(v) 133 if err != nil { 134 return nil, err 135 } 136 137 arrTranslated[i] = durArr 138 default: 139 arrTranslated[i] = v 140 } 141 } 142 143 return arrTranslated, nil 144 } 145 146 // DurationToNanosBytes transforms a json byte slice with Duration keys into Nanos 147 func DurationToNanosBytes(r io.Reader) ([]byte, error) { 148 var dict map[string]interface{} 149 d := json.NewDecoder(r) 150 d.UseNumber() 151 if err := d.Decode(&dict); err != nil { 152 return nil, fmt.Errorf("error decoding JSON: %s", err.Error()) 153 } 154 155 ret, err := DurationToNanosMap(dict) 156 if err != nil { 157 return nil, fmt.Errorf("error converting duration to nanos: %s", err.Error()) 158 } 159 160 b, err := json.Marshal(ret) 161 if err != nil { 162 return nil, fmt.Errorf("error unmarshaling JSON: %s", err.Error()) 163 } 164 return b, nil 165 } 166 167 // DurationToNanosMap transforms keys with a Duration into Nanos 168 func DurationToNanosMap(input map[string]interface{}) (map[string]interface{}, error) { 169 dictTranslated := make(map[string]interface{}, len(input)) 170 171 for k, v := range input { 172 if strings.HasSuffix(k, durationSuffix) { 173 newKey := strings.Replace(k, durationSuffix, nanosSuffix, 1) 174 175 switch vv := v.(type) { 176 case string: 177 duration, err := time.ParseDuration(vv) 178 if err != nil { 179 return nil, err 180 } 181 182 dictTranslated[newKey] = duration.Nanoseconds() 183 case json.Number: 184 // json.Number when using a json decoder with UseNumber() 185 // Assume given number is in nanos 186 vvNum, err := vv.Int64() 187 if err != nil { 188 return nil, err 189 } 190 191 dictTranslated[newKey] = vvNum 192 case float64: 193 // float64 when unmarshaling without UseNumber() 194 // Assume given number is in nanos 195 dictTranslated[newKey] = int64(vv) 196 default: 197 // Has Duration suffix, but is not string or number 198 return nil, errDurationType 199 } 200 } else { 201 switch vv := v.(type) { 202 case map[string]interface{}: 203 durMap, err := DurationToNanosMap(vv) 204 if err != nil { 205 return nil, err 206 } 207 208 dictTranslated[k] = durMap 209 case []interface{}: 210 durArr, err := durationToNanosArray(vv) 211 if err != nil { 212 return nil, err 213 } 214 215 dictTranslated[k] = durArr 216 default: 217 dictTranslated[k] = vv 218 } 219 } 220 } 221 222 return dictTranslated, nil 223 } 224 225 func durationToNanosArray(input []interface{}) ([]interface{}, error) { 226 arrTranslated := make([]interface{}, len(input)) 227 for i, elem := range input { 228 switch v := elem.(type) { 229 case map[string]interface{}: 230 durMap, err := DurationToNanosMap(v) 231 if err != nil { 232 return nil, err 233 } 234 235 arrTranslated[i] = durMap 236 case []interface{}: 237 durArr, err := durationToNanosArray(v) 238 if err != nil { 239 return nil, err 240 } 241 242 arrTranslated[i] = durArr 243 default: 244 arrTranslated[i] = v 245 } 246 } 247 248 return arrTranslated, nil 249 }