-
Notifications
You must be signed in to change notification settings - Fork 8
/
jmap_get.go
266 lines (227 loc) · 6.36 KB
/
jmap_get.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package trace2receiver
import (
"encoding/json"
"fmt"
"time"
)
// Dictionary used to decode a JSON object record containing a
// single Trace2 Event record into a generic map. This typedef
// is mainly to hide the awkward GOLANG syntax.
type jmap map[string]interface{}
// Optional keys/value pairs return a pointer to the value so
// that a non-present key is returned as a NULL pointer rather
// than an overloaded zero value. If the map value is of a
// different type than requested, we return an error rather than
// trying to convert it (since we are trying to parse and validate
// a known document format.)
//
// All variants follow these rules:
// Returns (p, nil) when successful.
// Returns (nil, nil) if not present.
// Returns (nil, err) if the value in the map is of a different type.
// Get an optional string value from the map.
func (jm *jmap) getOptionalString(key string) (*string, error) {
var v interface{}
var ok bool
if v, ok = (*jm)[key]; !ok {
return nil, nil
}
ps := new(string)
switch v := v.(type) {
case string:
*ps = v
return ps, nil
default:
return nil, fmt.Errorf("optional key '%s' does not have string value", key)
}
}
func (jm *jmap) getOptionalInt64(key string) (*int64, error) {
var v interface{}
var ok bool
if v, ok = (*jm)[key]; !ok {
return nil, nil
}
pi := new(int64)
// Allow both int and int64 in case we are unit testing. Allow float64
// because the generic JSON decoder always creates floats (because JavaScript
// does not have integer data types), so we have to convert it back.
switch v := v.(type) {
case int64:
*pi = v
return pi, nil
case int:
*pi = int64(v)
return pi, nil
case float64:
*pi = int64(v)
return pi, nil
default:
return nil, fmt.Errorf("key '%s' does not have an integer value", key)
}
}
// Required keys/value pairs return the value or an hard error if
// the key is not present or the map value is of a different type
// than requested.
//
// All variants follow these rules:
// Returns (p, nil) when successful.
// Returns (nil, err) if not present.
// Returns (nil, err) if value type wrong.
func (jm *jmap) getRequired(key string) (interface{}, error) {
var v interface{}
var ok bool
if v, ok = (*jm)[key]; !ok {
return nil, fmt.Errorf("key '%s' not present in Trace2 event", key)
}
return v, nil
}
func (jm *jmap) getRequiredBool(key string) (bool, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return false, err
}
switch v := v.(type) {
case bool:
return v, nil
default:
return false, fmt.Errorf("key '%s' does not have bool value", key)
}
}
func (jm *jmap) getRequiredString(key string) (string, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return "", err
}
switch v := v.(type) {
case string:
return v, nil
default:
return "", fmt.Errorf("key '%s' does not have string value", key)
}
}
func (jm *jmap) getRequiredInt64(key string) (int64, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return 0, err
}
// Allow both int and int64 in case we are unit testing. Allow float64
// because the generic JSON decoder always creates floats (because JavaScript
// does not have integer data types), so we have to convert it back.
switch v := v.(type) {
case int64:
return v, nil
case int:
return int64(v), nil
case float64:
return int64(v), nil
default:
return 0, fmt.Errorf("key '%s' does not have an integer value", key)
}
}
func (jm *jmap) getRequiredStringOrInt64(key string) (interface{}, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return 0, err
}
// Allow both int and int64 in case we are unit testing. Allow float64
// because the generic JSON decoder always creates floats (because JavaScript
// does not have integer data types), so we have to convert it back.
switch v := v.(type) {
case int64:
return v, nil
case int:
return int64(v), nil
case float64:
return int64(v), nil
case string:
return v, nil
default:
return 0, fmt.Errorf("key '%s' does not have an integer or string value", key)
}
}
func (jm *jmap) getRequiredFloat64(key string) (float64, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return 0, err
}
// Allow both int and int64 in case the JSON writer is sloppy and doesn't
// add a trailing .0 for whole numbers. This is primarily for unit testing
// since the generic JSON decoder always creates floats because of JavaScript
// limitations.
switch v := v.(type) {
case float64:
return v, nil
case int64:
return float64(v), nil
case int:
return float64(v), nil
default:
return 0.0, fmt.Errorf("key '%s' does not have an float value", key)
}
}
func (jm *jmap) getRequiredTime(key string) (time.Time, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return time.Time{}, err
}
switch v := v.(type) {
case string:
t, err := time.Parse("2006-01-02T15:04:05.999999Z", v)
if err == nil {
return t, err
}
// A version of GCM sends "+00:00" for the TZ rather than a "Z".
t, err = time.Parse("2006-01-02T15:04:05.999999-07:00", v)
return t, err
default:
return time.Time{}, fmt.Errorf("key '%s' does not have string value", key)
}
}
// Extract required JSON array value.
//
// We usually use this for "argv", but leave it as an []interface{}
// type rather than assuming it is an []string (because we'll probably
// need that later).
func (jm *jmap) getRequiredArray(key string) ([]interface{}, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return nil, err
}
switch v := v.(type) {
case []interface{}:
return v, nil
case []string:
// Implicitly case []string back to []interface{} for unit tests.
vv := make([]interface{}, len(v))
for k := range v {
vv[k] = v[k]
}
return vv, nil
default:
return nil, fmt.Errorf("key '%s' is not an array", key)
}
}
func (jm *jmap) getRequiredJsonValue(key string) (interface{}, error) {
var v interface{}
var err error
if v, err = jm.getRequired(key); err != nil {
return nil, err
}
switch v := v.(type) {
case interface{}:
_, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("key '%s' is not a JSON object", key)
}
return v, nil
default:
return nil, fmt.Errorf("key '%s' is not a JSON object", key)
}
}