• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pace / bricks / 12827718001

17 Jan 2025 10:57AM UTC coverage: 56.909% (-0.3%) from 57.237%
12827718001

Pull #384

github

monstermunchkin
Satisfy linters
Pull Request #384: Extend linting

478 of 946 new or added lines in 109 files covered. (50.53%)

133 existing lines in 53 files now uncovered.

5667 of 9958 relevant lines covered (56.91%)

21.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.71
/http/jsonapi/request.go
1
// This file is originating from https://github.com/google/jsonapi/
2
// To this file the license conditions of LICENSE apply.
3

4
package jsonapi
5

6
import (
7
        "bytes"
8
        "encoding/json"
9
        "errors"
10
        "fmt"
11
        "io"
12
        "reflect"
13
        "strconv"
14
        "strings"
15
        "time"
16

17
        "github.com/shopspring/decimal"
18
)
19

20
const (
21
        unsupportedStructTagMsg = "unsupported jsonapi tag annotation, %s"
22
)
23

24
var (
25
        // ErrInvalidTime is returned when a struct has a time.Time type field, but
26
        // the JSON value was not a unix timestamp integer.
27
        ErrInvalidTime = errors.New("only numbers can be parsed as dates, unix timestamps")
28
        // ErrInvalidISO8601 is returned when a struct has a time.Time type field and includes
29
        // "iso8601" in the tag spec, but the JSON value was not an ISO8601 timestamp string.
30
        ErrInvalidISO8601 = errors.New("only strings can be parsed as dates, ISO8601 timestamps")
31
        // ErrUnknownFieldNumberType is returned when the JSON value was a float
32
        // (numeric) but the Struct field was a non numeric type (i.e. not int, uint,
33
        // float, etc).
34
        ErrUnknownFieldNumberType = errors.New("the struct field was not of a known number type")
35
        // ErrInvalidType is returned when the given type is incompatible with the expected type.
36
        ErrInvalidType = errors.New("invalid type provided") // I wish we used punctuation.
37

38
)
39

40
// UnsupportedPtrTypeError is returned when the Struct field was a pointer but
41
// the JSON value was of a different type.
42
type UnsupportedPtrTypeError struct {
43
        rf          reflect.Value
44
        t           reflect.Type
45
        structField reflect.StructField
46
}
47

48
func (eupt UnsupportedPtrTypeError) Error() string {
4✔
49
        typeName := eupt.t.Elem().Name()
4✔
50
        kind := eupt.t.Elem().Kind()
4✔
51

4✔
52
        if kind.String() != "" && kind.String() != typeName {
4✔
53
                typeName = fmt.Sprintf("%s (%s)", typeName, kind.String())
×
54
        }
×
55

56
        return fmt.Sprintf(
4✔
57
                "jsonapi: Can't unmarshal %+v (%s) to struct field `%s`, which is a pointer to `%s`",
4✔
58
                eupt.rf, eupt.rf.Type().Kind(), eupt.structField.Name, typeName,
4✔
59
        )
4✔
60
}
61

62
func newErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField reflect.StructField) UnsupportedPtrTypeError {
4✔
63
        return UnsupportedPtrTypeError{rf, t, structField}
4✔
64
}
4✔
65

66
// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
67
// struct fields. This method supports single request payloads only, at the
68
// moment. Bulk creates and updates are not supported yet.
69
//
70
// Will Unmarshal embedded and sideloaded payloads.  The latter is only possible if the
71
// object graph is complete.  That is, in the "relationships" data there are type and id,
72
// keys that correspond to records in the "included" array.
73
//
74
// For example you could pass it, in, req.Body and, model, a BlogPost
75
// struct instance to populate in an http handler,
76
//
77
//        func CreateBlog(w http.ResponseWriter, r *http.Request) {
78
//                blog := new(Blog)
79
//
80
//                if err := jsonapi.UnmarshalPayload(r.Body, blog); err != nil {
81
//                        http.Error(w, err.Error(), 500)
82
//                        return
83
//                }
84
//
85
//                // ...do stuff with your blog...
86
//
87
//                w.Header().Set("Content-Type", jsonapi.MediaType)
88
//                w.WriteHeader(http.StatusCreated)
89
//
90
//                if err := jsonapi.MarshalPayload(w, blog); err != nil {
91
//                        http.Error(w, err.Error(), 500)
92
//                }
93
//        }
94
//
95
// Visit https://github.com/google/jsonapi#create for more info.
96
//
97
// model interface{} should be a pointer to a struct.
98
func UnmarshalPayload(in io.Reader, model interface{}) error {
45✔
99
        payload := new(OnePayload)
45✔
100

45✔
101
        if err := json.NewDecoder(in).Decode(payload); err != nil {
45✔
102
                return err
×
103
        }
×
104

105
        if payload.Included != nil {
47✔
106
                includedMap := make(map[string]*Node)
2✔
107

2✔
108
                for _, included := range payload.Included {
12✔
109
                        key := fmt.Sprintf("%s,%s", included.Type, included.ID)
10✔
110
                        includedMap[key] = included
10✔
111
                }
10✔
112

113
                return unmarshalNode(payload.Data, reflect.ValueOf(model), &includedMap)
2✔
114
        }
115

116
        return unmarshalNode(payload.Data, reflect.ValueOf(model), nil)
43✔
117
}
118

119
// UnmarshalManyPayload converts an io into a set of struct instances using
120
// jsonapi tags on the type's struct fields.
121
func UnmarshalManyPayload(in io.Reader, t reflect.Type) ([]interface{}, error) {
1✔
122
        payload := new(ManyPayload)
1✔
123

1✔
124
        if err := json.NewDecoder(in).Decode(payload); err != nil {
1✔
125
                return nil, err
×
126
        }
×
127

128
        models := []interface{}{}         // will be populated from the "data"
1✔
129
        includedMap := map[string]*Node{} // will be populate from the "included"
1✔
130

1✔
131
        if payload.Included != nil {
1✔
132
                for _, included := range payload.Included {
×
133
                        key := fmt.Sprintf("%s,%s", included.Type, included.ID)
×
134
                        includedMap[key] = included
×
135
                }
×
136
        }
137

138
        for _, data := range payload.Data {
3✔
139
                model := reflect.New(t.Elem())
2✔
140

2✔
141
                if err := unmarshalNode(data, model, &includedMap); err != nil {
2✔
142
                        return nil, err
×
143
                }
×
144

145
                models = append(models, model.Interface())
2✔
146
        }
147

148
        return models, nil
1✔
149
}
150

151
func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node) (err error) {
127✔
152
        defer func() {
254✔
153
                if r := recover(); r != nil {
130✔
154
                        err = fmt.Errorf("data is not a jsonapi representation of '%v': %q", model.Type(), r)
3✔
155
                }
3✔
156
        }()
157

158
        modelValue := model.Elem()
127✔
159
        modelType := modelValue.Type()
127✔
160

127✔
161
        var er error
127✔
162

127✔
163
        for i := 0; i < modelValue.NumField(); i++ {
782✔
164
                fieldType := modelType.Field(i)
655✔
165

655✔
166
                tag := fieldType.Tag.Get("jsonapi")
655✔
167
                if tag == "" {
689✔
168
                        continue
34✔
169
                }
170

171
                fieldValue := modelValue.Field(i)
621✔
172

621✔
173
                args := strings.Split(tag, ",")
621✔
174
                if len(args) < 1 {
621✔
175
                        er = ErrBadJSONAPIStructTag
×
176
                        break
×
177
                }
178

179
                annotation := args[0]
621✔
180

621✔
181
                if (annotation == annotationClientID && len(args) != 1) ||
621✔
182
                        (annotation != annotationClientID && len(args) < 2) {
622✔
183
                        er = ErrBadJSONAPIStructTag
1✔
184
                        break
1✔
185
                }
186

187
                if annotation == annotationPrimary {
731✔
188
                        if data.ID == "" {
139✔
189
                                continue
28✔
190
                        }
191

192
                        // Check the JSON API Type
193
                        if data.Type != args[1] {
82✔
194
                                er = fmt.Errorf(
×
NEW
195
                                        "trying to Unmarshal an object of type %#v, but %#v does not match",
×
196
                                        data.Type,
×
197
                                        args[1],
×
198
                                )
×
NEW
199

×
UNCOV
200
                                break
×
201
                        }
202

203
                        // ID will have to be transmitted as astring per the JSON API spec
204
                        v := reflect.ValueOf(data.ID)
82✔
205

82✔
206
                        // Deal with PTRS
82✔
207
                        var kind reflect.Kind
82✔
208
                        if fieldValue.Kind() == reflect.Ptr {
89✔
209
                                kind = fieldType.Type.Elem().Kind()
7✔
210
                        } else {
82✔
211
                                kind = fieldType.Type.Kind()
75✔
212
                        }
75✔
213

214
                        // Handle String case
215
                        if kind == reflect.String {
91✔
216
                                assign(fieldValue, v)
9✔
217
                                continue
9✔
218
                        }
219

220
                        // Value was not a string... only other supported type was a numeric,
221
                        // which would have been sent as a float value.
222
                        floatValue, err := strconv.ParseFloat(data.ID, 64)
73✔
223
                        if err != nil {
74✔
224
                                // Could not convert the value in the "id" attr to a float
1✔
225
                                er = ErrBadJSONAPIID
1✔
226
                                break
1✔
227
                        }
228

229
                        // Convert the numeric float to one of the supported ID numeric types
230
                        // (int[8,16,32,64] or uint[8,16,32,64])
231
                        idValue, err := handleNumeric(floatValue, fieldType.Type, fieldValue)
72✔
232
                        if err != nil {
72✔
233
                                // We had a JSON float (numeric), but our field was not one of the
×
234
                                // allowed numeric types
×
235
                                er = ErrBadJSONAPIID
×
236
                                break
×
237
                        }
238

239
                        assign(fieldValue, idValue)
72✔
240
                } else if annotation == annotationClientID {
594✔
241
                        if data.ClientID == "" {
146✔
242
                                continue
61✔
243
                        }
244

245
                        fieldValue.Set(reflect.ValueOf(data.ClientID))
24✔
246
                } else if annotation == annotationAttribute {
772✔
247
                        attributes := data.Attributes
348✔
248

348✔
249
                        if attributes == nil || len(data.Attributes) == 0 {
364✔
250
                                continue
16✔
251
                        }
252

253
                        attribute := attributes[args[1]]
332✔
254

332✔
255
                        // continue if the attribute was not included in the request
332✔
256
                        if attribute == nil {
422✔
257
                                continue
90✔
258
                        }
259

260
                        structField := fieldType
242✔
261

242✔
262
                        value, err := unmarshalAttribute(attribute, args, structField, fieldValue)
242✔
263
                        if err != nil {
253✔
264
                                er = err
11✔
265
                                break
11✔
266
                        }
267

268
                        if value.IsValid() {
461✔
269
                                assign(fieldValue, value)
230✔
270
                        }
230✔
271
                } else if annotation == annotationRelation {
152✔
272
                        isSlice := fieldValue.Type().Kind() == reflect.Slice
76✔
273

76✔
274
                        if data.Relationships == nil || data.Relationships[args[1]] == nil {
104✔
275
                                continue
28✔
276
                        }
277

278
                        if isSlice {
74✔
279
                                // to-many relationship
26✔
280
                                relationship := new(RelationshipManyNode)
26✔
281

26✔
282
                                buf := bytes.NewBuffer(nil)
26✔
283

26✔
284
                                if err := json.NewEncoder(buf).Encode(data.Relationships[args[1]]); err != nil {
26✔
NEW
285
                                        return err
×
NEW
286
                                }
×
287

288
                                if err := json.NewDecoder(buf).Decode(relationship); err != nil {
26✔
NEW
289
                                        return err
×
NEW
290
                                }
×
291

292
                                data := relationship.Data
26✔
293
                                models := reflect.New(fieldValue.Type()).Elem()
26✔
294

26✔
295
                                for _, n := range data {
76✔
296
                                        m := reflect.New(fieldValue.Type().Elem().Elem())
50✔
297

50✔
298
                                        if err := unmarshalNode(
50✔
299
                                                fullNode(n, included),
50✔
300
                                                m,
50✔
301
                                                included,
50✔
302
                                        ); err != nil {
50✔
303
                                                er = err
×
304
                                                break
×
305
                                        }
306

307
                                        models = reflect.Append(models, m)
50✔
308
                                }
309

310
                                fieldValue.Set(models)
26✔
311
                        } else {
22✔
312
                                // to-one relationships
22✔
313
                                relationship := new(RelationshipOneNode)
22✔
314

22✔
315
                                buf := bytes.NewBuffer(nil)
22✔
316

22✔
317
                                if err := json.NewEncoder(buf).Encode(data.Relationships[args[1]]); err != nil {
22✔
NEW
318
                                        return err
×
NEW
319
                                }
×
320

321
                                if err := json.NewDecoder(buf).Decode(relationship); err != nil {
22✔
NEW
322
                                        return err
×
NEW
323
                                }
×
324

325
                                /*
326
                                        http://jsonapi.org/format/#document-resource-object-relationships
327
                                        http://jsonapi.org/format/#document-resource-object-linkage
328
                                        relationship can have a data node set to null (e.g. to disassociate the relationship)
329
                                        so unmarshal and set fieldValue only if data obj is not null
330
                                */
331
                                if relationship.Data == nil {
23✔
332
                                        continue
1✔
333
                                }
334

335
                                m := reflect.New(fieldValue.Type().Elem())
21✔
336
                                if err := unmarshalNode(
21✔
337
                                        fullNode(relationship.Data, included),
21✔
338
                                        m,
21✔
339
                                        included,
21✔
340
                                ); err != nil {
21✔
341
                                        er = err
×
342
                                        break
×
343
                                }
344

345
                                fieldValue.Set(m)
21✔
346
                        }
UNCOV
347
                } else {
×
348
                        er = fmt.Errorf(unsupportedStructTagMsg, annotation)
×
349
                }
×
350
        }
351

352
        return er
124✔
353
}
354

355
func fullNode(n *Node, included *map[string]*Node) *Node {
71✔
356
        includedKey := fmt.Sprintf("%s,%s", n.Type, n.ID)
71✔
357

71✔
358
        if included != nil && (*included)[includedKey] != nil {
95✔
359
                return (*included)[includedKey]
24✔
360
        }
24✔
361

362
        return n
47✔
363
}
364

365
// assign will take the value specified and assign it to the field; if
366
// field is expecting a ptr assign will assign a ptr.
367
func assign(field, value reflect.Value) {
311✔
368
        value = reflect.Indirect(value)
311✔
369

311✔
370
        if field.Kind() == reflect.Ptr {
341✔
371
                // initialize pointer so it's value
30✔
372
                // can be set by assignValue
30✔
373
                field.Set(reflect.New(field.Type().Elem()))
30✔
374

30✔
375
                field = field.Elem()
30✔
376
        }
30✔
377

378
        assignValue(field, value)
311✔
379
}
380

381
// assign assigns the specified value to the field,
382
// expecting both values not to be pointer types.
383
func assignValue(field, value reflect.Value) {
311✔
384
        switch field.Kind() {
311✔
385
        case reflect.Int, reflect.Int8, reflect.Int16,
386
                reflect.Int32, reflect.Int64:
109✔
387
                field.SetInt(value.Int())
109✔
388
        case reflect.Uint, reflect.Uint8, reflect.Uint16,
389
                reflect.Uint32, reflect.Uint64, reflect.Uintptr:
28✔
390
                field.SetUint(value.Uint())
28✔
391
        case reflect.Float32, reflect.Float64:
2✔
392
                field.SetFloat(value.Float())
2✔
393
        case reflect.String:
133✔
394
                field.SetString(value.String())
133✔
395
        case reflect.Bool:
2✔
396
                field.SetBool(value.Bool())
2✔
397
        default:
37✔
398
                field.Set(value)
37✔
399
        }
400
}
401

402
func unmarshalAttribute(
403
        rawAttribute json.RawMessage,
404
        args []string,
405
        structField reflect.StructField,
406
        fieldValue reflect.Value,
407
) (reflect.Value, error) {
242✔
408
        var attribute interface{}
242✔
409

242✔
410
        if err := json.Unmarshal(rawAttribute, &attribute); err != nil {
242✔
411
                return reflect.Value{}, err
×
412
        }
×
413

414
        value := reflect.ValueOf(attribute)
242✔
415
        fieldType := structField.Type
242✔
416

242✔
417
        // decimal.Decimal and *decimal.Decimal
242✔
418
        if fieldValue.Type() == reflect.TypeOf(decimal.Decimal{}) ||
242✔
419
                fieldValue.Type() == reflect.TypeOf(new(decimal.Decimal)) {
253✔
420
                return handleDecimal(rawAttribute)
11✔
421
        }
11✔
422

423
        // map[string][]string
424
        if fieldValue.Type() == reflect.TypeOf(map[string][]string{}) {
233✔
425
                return handleMapStringSlice(rawAttribute)
2✔
426
        }
2✔
427

428
        // Handle field of type time.Time
429
        if fieldValue.Type() == reflect.TypeOf(time.Time{}) ||
229✔
430
                fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
247✔
431
                return handleTime(attribute, args, fieldValue)
18✔
432
        }
18✔
433

434
        // Handle field of type struct
435
        if fieldValue.Type().Kind() == reflect.Struct {
213✔
436
                return handleStruct(attribute, fieldValue)
2✔
437
        }
2✔
438

439
        // Handle field containing slices
440
        if fieldValue.Type().Kind() == reflect.Slice {
212✔
441
                value = reflect.New(fieldValue.Type())
3✔
442
                return value, json.Unmarshal(rawAttribute, value.Interface())
3✔
443
        }
3✔
444

445
        // JSON value was a float (numeric)
446
        if value.Kind() == reflect.Float64 {
274✔
447
                return handleNumeric(attribute, fieldType, fieldValue)
68✔
448
        }
68✔
449

450
        // Field was a Pointer type
451
        if fieldValue.Kind() == reflect.Ptr {
153✔
452
                return handlePointer(attribute, args, fieldType, fieldValue, structField)
15✔
453
        }
15✔
454

455
        // As a final catch-all, ensure types line up to avoid a runtime panic.
456
        if fieldValue.Kind() != value.Kind() {
125✔
457
                return value, fmt.Errorf("got value %q expected type %v: %w", value, fieldType, ErrInvalidType)
2✔
458
        }
2✔
459

460
        return value, nil
121✔
461
}
462

463
func handleDecimal(attribute json.RawMessage) (reflect.Value, error) {
11✔
464
        var dec decimal.Decimal
11✔
465

11✔
466
        if err := json.Unmarshal(attribute, &dec); err != nil {
11✔
NEW
467
                return reflect.Value{}, fmt.Errorf("can't decode decimal from value %q: %w", string(attribute), err)
×
UNCOV
468
        }
×
469

470
        return reflect.ValueOf(dec), nil
11✔
471
}
472

473
func handleMapStringSlice(attribute json.RawMessage) (reflect.Value, error) {
2✔
474
        var m map[string][]string
2✔
475

2✔
476
        if err := json.Unmarshal(attribute, &m); err != nil {
3✔
477
                return reflect.Value{}, fmt.Errorf("can't decode map string slice from value %q: %w", string(attribute), err)
1✔
478
        }
1✔
479

480
        return reflect.ValueOf(m), nil
1✔
481
}
482

483
func handleTime(attribute interface{}, args []string, fieldValue reflect.Value) (reflect.Value, error) {
18✔
484
        var isIso8601 bool
18✔
485

18✔
486
        v := reflect.ValueOf(attribute)
18✔
487

18✔
488
        if len(args) > 2 {
26✔
489
                for _, arg := range args[2:] {
16✔
490
                        if arg == annotationISO8601 {
16✔
491
                                isIso8601 = true
8✔
492
                        }
8✔
493
                }
494
        }
495

496
        if isIso8601 {
26✔
497
                var tm string
8✔
498
                if v.Kind() == reflect.String {
16✔
499
                        tm, _ = v.Interface().(string)
8✔
500
                } else {
8✔
501
                        return reflect.ValueOf(time.Now()), ErrInvalidISO8601
×
502
                }
×
503

504
                t, err := time.Parse(iso8601TimeFormat, tm)
8✔
505
                if err != nil {
9✔
506
                        return reflect.ValueOf(time.Now()), ErrInvalidISO8601
1✔
507
                }
1✔
508

509
                if fieldValue.Kind() == reflect.Ptr {
9✔
510
                        return reflect.ValueOf(&t), nil
2✔
511
                }
2✔
512

513
                return reflect.ValueOf(t), nil
5✔
514
        }
515

516
        var at int64
10✔
517

10✔
518
        if v.Kind() == reflect.Float64 {
18✔
519
                atTmp, _ := v.Interface().(float64)
8✔
520
                at = int64(atTmp)
8✔
521
        } else if v.Kind() == reflect.Int {
10✔
522
                at = v.Int()
×
523
        } else {
2✔
524
                return reflect.ValueOf(time.Now()), ErrInvalidTime
2✔
525
        }
2✔
526

527
        t := time.Unix(at, 0)
8✔
528

8✔
529
        return reflect.ValueOf(t), nil
8✔
530
}
531

532
func handleNumeric(
533
        attribute interface{},
534
        fieldType reflect.Type,
535
        fieldValue reflect.Value,
536
) (reflect.Value, error) {
140✔
537
        v := reflect.ValueOf(attribute)
140✔
538
        floatValue, _ := v.Interface().(float64)
140✔
539

140✔
540
        var kind reflect.Kind
140✔
541
        if fieldValue.Kind() == reflect.Ptr {
150✔
542
                kind = fieldType.Elem().Kind()
10✔
543
        } else {
140✔
544
                kind = fieldType.Kind()
130✔
545
        }
130✔
546

547
        var numericValue reflect.Value
140✔
548

140✔
549
        switch kind {
140✔
550
        case reflect.Int:
109✔
551
                n := int(floatValue)
109✔
552
                numericValue = reflect.ValueOf(&n)
109✔
553
        case reflect.Int8:
×
554
                n := int8(floatValue)
×
555
                numericValue = reflect.ValueOf(&n)
×
556
        case reflect.Int16:
×
557
                n := int16(floatValue)
×
558
                numericValue = reflect.ValueOf(&n)
×
559
        case reflect.Int32:
×
560
                n := int32(floatValue)
×
561
                numericValue = reflect.ValueOf(&n)
×
562
        case reflect.Int64:
×
563
                n := int64(floatValue)
×
564
                numericValue = reflect.ValueOf(&n)
×
565
        case reflect.Uint:
×
566
                n := uint(floatValue)
×
567
                numericValue = reflect.ValueOf(&n)
×
568
        case reflect.Uint8:
×
569
                n := uint8(floatValue)
×
570
                numericValue = reflect.ValueOf(&n)
×
571
        case reflect.Uint16:
×
572
                n := uint16(floatValue)
×
573
                numericValue = reflect.ValueOf(&n)
×
574
        case reflect.Uint32:
×
575
                n := uint32(floatValue)
×
576
                numericValue = reflect.ValueOf(&n)
×
577
        case reflect.Uint64:
28✔
578
                n := uint64(floatValue)
28✔
579
                numericValue = reflect.ValueOf(&n)
28✔
580
        case reflect.Float32:
1✔
581
                n := float32(floatValue)
1✔
582
                numericValue = reflect.ValueOf(&n)
1✔
583
        case reflect.Float64:
1✔
584
                n := floatValue
1✔
585
                numericValue = reflect.ValueOf(&n)
1✔
586
        default:
1✔
587
                return reflect.Value{}, ErrUnknownFieldNumberType
1✔
588
        }
589

590
        return numericValue, nil
139✔
591
}
592

593
func handlePointer(
594
        attribute interface{},
595
        _ []string,
596
        fieldType reflect.Type,
597
        fieldValue reflect.Value,
598
        structField reflect.StructField,
599
) (reflect.Value, error) {
15✔
600
        t := fieldValue.Type()
15✔
601

15✔
602
        var concreteVal reflect.Value
15✔
603

15✔
604
        if attribute == nil {
16✔
605
                return reflect.ValueOf(attribute), nil
1✔
606
        }
1✔
607

608
        switch cVal := attribute.(type) {
14✔
609
        case string:
3✔
610
                concreteVal = reflect.ValueOf(&cVal)
3✔
611
        case bool:
3✔
612
                concreteVal = reflect.ValueOf(&cVal)
3✔
613
        case complex64, complex128, uintptr:
×
614
                concreteVal = reflect.ValueOf(&cVal)
×
615
        case map[string]interface{}:
7✔
616
                var err error
7✔
617

7✔
618
                concreteVal, err = handleStruct(attribute, fieldValue)
7✔
619
                if err != nil {
9✔
620
                        return reflect.Value{}, newErrUnsupportedPtrType(
2✔
621
                                reflect.ValueOf(attribute), fieldType, structField)
2✔
622
                }
2✔
623

624
                return concreteVal, err
5✔
625
        default:
1✔
626
                return reflect.Value{}, newErrUnsupportedPtrType(
1✔
627
                        reflect.ValueOf(attribute), fieldType, structField)
1✔
628
        }
629

630
        if t != concreteVal.Type() {
7✔
631
                return reflect.Value{}, newErrUnsupportedPtrType(
1✔
632
                        reflect.ValueOf(attribute), fieldType, structField)
1✔
633
        }
1✔
634

635
        return concreteVal, nil
5✔
636
}
637

638
func handleStruct(
639
        attribute interface{},
640
        fieldValue reflect.Value,
641
) (reflect.Value, error) {
9✔
642
        data, err := json.Marshal(attribute)
9✔
643
        if err != nil {
9✔
644
                return reflect.Value{}, err
×
645
        }
×
646

647
        node := new(Node)
9✔
648
        if err := json.Unmarshal(data, &node.Attributes); err != nil {
9✔
649
                return reflect.Value{}, err
×
650
        }
×
651

652
        var model reflect.Value
9✔
653
        if fieldValue.Kind() == reflect.Ptr {
16✔
654
                model = reflect.New(fieldValue.Type().Elem())
7✔
655
        } else {
9✔
656
                model = reflect.New(fieldValue.Type())
2✔
657
        }
2✔
658

659
        if err := unmarshalNode(node, model, nil); err != nil {
11✔
660
                return reflect.Value{}, err
2✔
661
        }
2✔
662

663
        return model, nil
7✔
664
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc