github.com/seeker-insurance/kit@v0.0.13/jsonapi/request.go (about) 1 package jsonapi 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "reflect" 10 "strconv" 11 "strings" 12 "time" 13 ) 14 15 const ( 16 unsuportedStructTagMsg = "Unsupported jsonapi tag annotation, %s" 17 ) 18 19 var ( 20 // ErrInvalidTime is returned when a struct has a time.Time type field, but 21 // the JSON value was not a unix timestamp integer. 22 ErrInvalidTime = errors.New("Only numbers can be parsed as dates, unix timestamps") 23 // ErrInvalidISO8601 is returned when a struct has a time.Time type field and includes 24 // "iso8601" in the tag spec, but the JSON value was not an ISO8601 timestamp string. 25 ErrInvalidISO8601 = errors.New("Only strings can be parsed as dates, ISO8601 timestamps") 26 // ErrUnknownFieldNumberType is returned when the JSON value was a float 27 // (numeric) but the Struct field was a non numeric type (i.e. not int, uint, 28 // float, etc) 29 ErrUnknownFieldNumberType = errors.New("The struct field was not of a known number type") 30 // ErrUnsupportedPtrType is returned when the Struct field was a pointer but 31 // the JSON value was of a different type 32 ErrUnsupportedPtrType = errors.New("Pointer type in struct is not supported") 33 // ErrInvalidType is returned when the given type is incompatible with the expected type. 34 ErrInvalidType = errors.New("Invalid type provided") // I wish we used punctuation. 35 ) 36 37 // UnmarshalPayload converts an io into a struct instance using jsonapi tags on 38 // struct fields. This method supports single request payloads only, at the 39 // moment. Bulk creates and updates are not supported yet. 40 // 41 // Will Unmarshal embedded and sideloaded payloads. The latter is only possible if the 42 // object graph is complete. That is, in the "relationships" data there are type and id, 43 // keys that correspond to records in the "included" array. 44 // 45 // For example you could pass it, in, req.Body and, model, a BlogPost 46 // struct instance to populate in an http handler, 47 // 48 // func CreateBlog(w http.ResponseWriter, r *http.Request) { 49 // blog := new(Blog) 50 // 51 // if err := jsonapi.UnmarshalPayload(r.Body, blog); err != nil { 52 // http.Error(w, err.Error(), 500) 53 // return 54 // } 55 // 56 // // ...do stuff with your blog... 57 // 58 // w.Header().Set("Content-Type", jsonapi.MediaType) 59 // w.WriteHeader(201) 60 // 61 // if err := jsonapi.MarshalPayload(w, blog); err != nil { 62 // http.Error(w, err.Error(), 500) 63 // } 64 // } 65 // 66 // 67 // Visit https://github.com/google/jsonapi#create for more info. 68 // 69 // model interface{} should be a pointer to a struct. 70 func UnmarshalPayload(in io.Reader, model interface{}) error { 71 payload := new(OnePayload) 72 73 if err := json.NewDecoder(in).Decode(payload); err != nil { 74 return err 75 } 76 77 if payload.Included != nil { 78 includedMap := make(map[string]*Node) 79 for _, included := range payload.Included { 80 key := fmt.Sprintf("%s,%s", included.Type, included.ID) 81 includedMap[key] = included 82 } 83 84 return unmarshalNode(payload.Data, reflect.ValueOf(model), &includedMap) 85 } 86 return unmarshalNode(payload.Data, reflect.ValueOf(model), nil) 87 } 88 89 // UnmarshalManyPayload converts an io into a set of struct instances using 90 // jsonapi tags on the type's struct fields. 91 func UnmarshalManyPayload(in io.Reader, t reflect.Type) ([]interface{}, error) { 92 payload := new(ManyPayload) 93 94 if err := json.NewDecoder(in).Decode(payload); err != nil { 95 return nil, err 96 } 97 98 models := []interface{}{} // will be populated from the "data" 99 includedMap := map[string]*Node{} // will be populate from the "included" 100 101 if payload.Included != nil { 102 for _, included := range payload.Included { 103 key := fmt.Sprintf("%s,%s", included.Type, included.ID) 104 includedMap[key] = included 105 } 106 } 107 108 for _, data := range payload.Data { 109 model := reflect.New(t.Elem()) 110 err := unmarshalNode(data, model, &includedMap) 111 if err != nil { 112 return nil, err 113 } 114 models = append(models, model.Interface()) 115 } 116 117 return models, nil 118 } 119 120 // unmarshalNode handles embedded struct models from top to down. 121 // it loops through the struct fields, handles attributes/relations at that level first 122 // the handling the embedded structs are done last, so that you get the expected composition behavior 123 // data (*Node) attributes are cleared on each success. 124 // relations/sideloaded models use deeply copied Nodes (since those sideloaded models can be referenced in multiple relations) 125 func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node) (err error) { 126 defer func() { 127 if r := recover(); r != nil { 128 err = fmt.Errorf("data is not a jsonapi representation of '%v'", model.Type()) 129 } 130 }() 131 132 modelValue := model.Elem() 133 modelType := model.Type().Elem() 134 135 type embedded struct { 136 structField, model reflect.Value 137 } 138 embeddeds := []*embedded{} 139 140 for i := 0; i < modelValue.NumField(); i++ { 141 fieldType := modelType.Field(i) 142 fieldValue := modelValue.Field(i) 143 tag := fieldType.Tag.Get(annotationJSONAPI) 144 145 // handle explicit ignore annotation 146 if shouldIgnoreField(tag) { 147 continue 148 } 149 150 // TODO: support embed tags 151 if shouldTreatEmbeded(tag) { 152 continue 153 } 154 155 // handles embedded structs 156 if isEmbeddedStruct(fieldType) { 157 embeddeds = append(embeddeds, 158 &embedded{ 159 model: reflect.ValueOf(fieldValue.Addr().Interface()), 160 structField: fieldValue, 161 }, 162 ) 163 continue 164 } 165 166 // handles pointers to embedded structs 167 if isEmbeddedStructPtr(fieldType) { 168 embeddeds = append(embeddeds, 169 &embedded{ 170 model: reflect.ValueOf(fieldValue.Interface()), 171 structField: fieldValue, 172 }, 173 ) 174 continue 175 } 176 177 // handle tagless; after handling embedded structs (which could be tagless) 178 if tag == "" { 179 continue 180 } 181 182 args := strings.Split(tag, annotationSeperator) 183 // require atleast 1 184 if len(args) < 1 { 185 return ErrBadJSONAPIStructTag 186 } 187 188 // args[0] == annotation 189 switch args[0] { 190 case annotationClientID: 191 if err := handleClientIDUnmarshal(data, args, fieldValue); err != nil { 192 return err 193 } 194 case annotationPrimary: 195 if err := handlePrimaryUnmarshal(data, args, fieldType, fieldValue); err != nil { 196 return err 197 } 198 case annotationAttribute: 199 if err := handleAttributeUnmarshal(data, args, fieldType, fieldValue); err != nil { 200 return err 201 } 202 case annotationRelation: 203 if err := handleRelationUnmarshal(data, args, fieldValue, included); err != nil { 204 return err 205 } 206 default: 207 return fmt.Errorf(unsuportedStructTagMsg, args[0]) 208 } 209 } 210 211 // handle embedded last 212 for _, em := range embeddeds { 213 // if nil, need to construct and rollback accordingly 214 if em.model.IsNil() { 215 copy := deepCopyNode(data) 216 tmp := reflect.New(em.model.Type().Elem()) 217 if err := unmarshalNode(copy, tmp, included); err != nil { 218 return err 219 } 220 221 // had changes; assign value to struct field, replace orig node (data) w/ mutated copy 222 if !reflect.DeepEqual(copy, data) { 223 assign(em.structField, tmp) 224 data = copy 225 } 226 } else { 227 // handle non-nil scenarios 228 if err := unmarshalNode(data, em.model, included); err != nil { 229 return err 230 } 231 } 232 } 233 234 return nil 235 } 236 237 func handleClientIDUnmarshal(data *Node, args []string, fieldValue reflect.Value) error { 238 if len(args) != 1 { 239 return ErrBadJSONAPIStructTag 240 } 241 242 if data.ClientID == "" { 243 return nil 244 } 245 246 // set value and clear clientID to denote it's already been processed 247 fieldValue.Set(reflect.ValueOf(data.ClientID)) 248 data.ClientID = "" 249 250 return nil 251 } 252 253 func handlePrimaryUnmarshal(data *Node, args []string, fieldType reflect.StructField, fieldValue reflect.Value) error { 254 if len(args) < 2 { 255 return ErrBadJSONAPIStructTag 256 } 257 258 if data.ID == "" { 259 return nil 260 } 261 262 // Check the JSON API Type 263 if data.Type != args[1] { 264 return fmt.Errorf( 265 "Trying to Unmarshal an object of type %#v, but %#v does not match", 266 data.Type, 267 args[1], 268 ) 269 } 270 271 // Deal with PTRS 272 var kind reflect.Kind 273 if fieldValue.Kind() == reflect.Ptr { 274 kind = fieldType.Type.Elem().Kind() 275 } else { 276 kind = fieldType.Type.Kind() 277 } 278 279 var idValue reflect.Value 280 281 // Handle String case 282 if kind == reflect.String { 283 // ID will have to be transmitted as a string per the JSON API spec 284 idValue = reflect.ValueOf(data.ID) 285 } else { 286 // Value was not a string... only other supported type was a numeric, 287 // which would have been sent as a float value. 288 floatValue, err := strconv.ParseFloat(data.ID, 64) 289 if err != nil { 290 // Could not convert the value in the "id" attr to a float 291 return ErrBadJSONAPIID 292 } 293 294 // Convert the numeric float to one of the supported ID numeric types 295 // (int[8,16,32,64] or uint[8,16,32,64]) 296 switch kind { 297 case reflect.Int: 298 n := int(floatValue) 299 idValue = reflect.ValueOf(&n) 300 case reflect.Int8: 301 n := int8(floatValue) 302 idValue = reflect.ValueOf(&n) 303 case reflect.Int16: 304 n := int16(floatValue) 305 idValue = reflect.ValueOf(&n) 306 case reflect.Int32: 307 n := int32(floatValue) 308 idValue = reflect.ValueOf(&n) 309 case reflect.Int64: 310 n := int64(floatValue) 311 idValue = reflect.ValueOf(&n) 312 case reflect.Uint: 313 n := uint(floatValue) 314 idValue = reflect.ValueOf(&n) 315 case reflect.Uint8: 316 n := uint8(floatValue) 317 idValue = reflect.ValueOf(&n) 318 case reflect.Uint16: 319 n := uint16(floatValue) 320 idValue = reflect.ValueOf(&n) 321 case reflect.Uint32: 322 n := uint32(floatValue) 323 idValue = reflect.ValueOf(&n) 324 case reflect.Uint64: 325 n := uint64(floatValue) 326 idValue = reflect.ValueOf(&n) 327 default: 328 // We had a JSON float (numeric), but our field was not one of the 329 // allowed numeric types 330 return ErrBadJSONAPIID 331 } 332 } 333 334 // set value and clear ID to denote it's already been processed 335 assign(fieldValue, idValue) 336 data.ID = "" 337 338 return nil 339 } 340 341 func handleRelationUnmarshal(data *Node, args []string, fieldValue reflect.Value, included *map[string]*Node) error { 342 if len(args) < 2 { 343 return ErrBadJSONAPIStructTag 344 } 345 346 if data.Relationships == nil || data.Relationships[args[1]] == nil { 347 return nil 348 } 349 350 // to-one relationships 351 handler := handleToOneRelationUnmarshal 352 isSlice := fieldValue.Type().Kind() == reflect.Slice 353 if isSlice { 354 // to-many relationship 355 handler = handleToManyRelationUnmarshal 356 } 357 358 v, err := handler(data.Relationships[args[1]], fieldValue.Type(), included) 359 if err != nil { 360 return err 361 } 362 // set only if there is a val since val can be null (e.g. to disassociate the relationship) 363 if v != nil { 364 fieldValue.Set(*v) 365 } 366 delete(data.Relationships, args[1]) 367 return nil 368 } 369 370 // to-one relationships 371 func handleToOneRelationUnmarshal(relationData interface{}, fieldType reflect.Type, included *map[string]*Node) (*reflect.Value, error) { 372 relationship := new(RelationshipOneNode) 373 374 buf := bytes.NewBuffer(nil) 375 json.NewEncoder(buf).Encode(relationData) 376 json.NewDecoder(buf).Decode(relationship) 377 378 m := reflect.New(fieldType.Elem()) 379 /* 380 http://jsonapi.org/format/#document-resource-object-relationships 381 http://jsonapi.org/format/#document-resource-object-linkage 382 relationship can have a data node set to null (e.g. to disassociate the relationship) 383 so unmarshal and set fieldValue only if data obj is not null 384 */ 385 if relationship.Data == nil { 386 return nil, nil 387 } 388 389 if err := unmarshalNode( 390 fullNode(relationship.Data, included), 391 m, 392 included, 393 ); err != nil { 394 return nil, err 395 } 396 397 return &m, nil 398 } 399 400 // to-many relationship 401 func handleToManyRelationUnmarshal(relationData interface{}, fieldType reflect.Type, included *map[string]*Node) (*reflect.Value, error) { 402 relationship := new(RelationshipManyNode) 403 404 buf := bytes.NewBuffer(nil) 405 json.NewEncoder(buf).Encode(relationData) 406 json.NewDecoder(buf).Decode(relationship) 407 408 models := reflect.New(fieldType).Elem() 409 410 rData := relationship.Data 411 for _, n := range rData { 412 m := reflect.New(fieldType.Elem().Elem()) 413 414 if err := unmarshalNode( 415 fullNode(n, included), 416 m, 417 included, 418 ); err != nil { 419 return nil, err 420 } 421 422 models = reflect.Append(models, m) 423 } 424 425 return &models, nil 426 } 427 428 // TODO: break this out into smaller funcs 429 func handleAttributeUnmarshal(data *Node, args []string, fieldType reflect.StructField, fieldValue reflect.Value) error { 430 if len(args) < 2 { 431 return ErrBadJSONAPIStructTag 432 } 433 attributes := data.Attributes 434 if attributes == nil || len(data.Attributes) == 0 { 435 return nil 436 } 437 438 var iso8601 bool 439 440 if len(args) > 2 { 441 for _, arg := range args[2:] { 442 if arg == annotationISO8601 { 443 iso8601 = true 444 } 445 } 446 } 447 448 val := attributes[args[1]] 449 450 // continue if the attribute was not included in the request 451 if val == nil { 452 return nil 453 } 454 455 v := reflect.ValueOf(val) 456 457 // Handle field of type time.Time 458 if fieldValue.Type() == reflect.TypeOf(time.Time{}) { 459 if iso8601 { 460 var tm string 461 if v.Kind() == reflect.String { 462 tm = v.Interface().(string) 463 } else { 464 return ErrInvalidISO8601 465 } 466 467 t, err := time.Parse(iso8601TimeFormat, tm) 468 if err != nil { 469 return ErrInvalidISO8601 470 } 471 472 fieldValue.Set(reflect.ValueOf(t)) 473 delete(data.Attributes, args[1]) 474 return nil 475 } 476 477 var at int64 478 479 if v.Kind() == reflect.Float64 { 480 at = int64(v.Interface().(float64)) 481 } else if v.Kind() == reflect.Int { 482 at = v.Int() 483 } else { 484 return ErrInvalidTime 485 } 486 487 t := time.Unix(at, 0) 488 489 fieldValue.Set(reflect.ValueOf(t)) 490 delete(data.Attributes, args[1]) 491 return nil 492 } 493 494 if fieldValue.Type() == reflect.TypeOf([]string{}) { 495 values := make([]string, v.Len()) 496 for i := 0; i < v.Len(); i++ { 497 values[i] = v.Index(i).Interface().(string) 498 } 499 500 fieldValue.Set(reflect.ValueOf(values)) 501 delete(data.Attributes, args[1]) 502 return nil 503 } 504 505 if fieldValue.Type() == reflect.TypeOf(new(time.Time)) { 506 if iso8601 { 507 var tm string 508 if v.Kind() == reflect.String { 509 tm = v.Interface().(string) 510 } else { 511 return ErrInvalidISO8601 512 513 } 514 515 v, err := time.Parse(iso8601TimeFormat, tm) 516 if err != nil { 517 return ErrInvalidISO8601 518 } 519 520 t := &v 521 522 fieldValue.Set(reflect.ValueOf(t)) 523 delete(data.Attributes, args[1]) 524 return nil 525 } 526 527 var at int64 528 529 if v.Kind() == reflect.Float64 { 530 at = int64(v.Interface().(float64)) 531 } else if v.Kind() == reflect.Int { 532 at = v.Int() 533 } else { 534 return ErrInvalidTime 535 } 536 537 v := time.Unix(at, 0) 538 t := &v 539 540 fieldValue.Set(reflect.ValueOf(t)) 541 delete(data.Attributes, args[1]) 542 return nil 543 } 544 545 // JSON value was a float (numeric) 546 if v.Kind() == reflect.Float64 { 547 floatValue := v.Interface().(float64) 548 549 // The field may or may not be a pointer to a numeric; the kind var 550 // will not contain a pointer type 551 var kind reflect.Kind 552 if fieldValue.Kind() == reflect.Ptr { 553 kind = fieldType.Type.Elem().Kind() 554 } else { 555 kind = fieldType.Type.Kind() 556 } 557 558 var numericValue reflect.Value 559 560 switch kind { 561 case reflect.Int: 562 n := int(floatValue) 563 numericValue = reflect.ValueOf(&n) 564 case reflect.Int8: 565 n := int8(floatValue) 566 numericValue = reflect.ValueOf(&n) 567 case reflect.Int16: 568 n := int16(floatValue) 569 numericValue = reflect.ValueOf(&n) 570 case reflect.Int32: 571 n := int32(floatValue) 572 numericValue = reflect.ValueOf(&n) 573 case reflect.Int64: 574 n := int64(floatValue) 575 numericValue = reflect.ValueOf(&n) 576 case reflect.Uint: 577 n := uint(floatValue) 578 numericValue = reflect.ValueOf(&n) 579 case reflect.Uint8: 580 n := uint8(floatValue) 581 numericValue = reflect.ValueOf(&n) 582 case reflect.Uint16: 583 n := uint16(floatValue) 584 numericValue = reflect.ValueOf(&n) 585 case reflect.Uint32: 586 n := uint32(floatValue) 587 numericValue = reflect.ValueOf(&n) 588 case reflect.Uint64: 589 n := uint64(floatValue) 590 numericValue = reflect.ValueOf(&n) 591 case reflect.Float32: 592 n := float32(floatValue) 593 numericValue = reflect.ValueOf(&n) 594 case reflect.Float64: 595 n := floatValue 596 numericValue = reflect.ValueOf(&n) 597 default: 598 return ErrUnknownFieldNumberType 599 } 600 601 assign(fieldValue, numericValue) 602 delete(data.Attributes, args[1]) 603 return nil 604 } 605 606 // Field was a Pointer type 607 if fieldValue.Kind() == reflect.Ptr { 608 var concreteVal reflect.Value 609 610 switch cVal := val.(type) { 611 case string: 612 concreteVal = reflect.ValueOf(&cVal) 613 case bool: 614 concreteVal = reflect.ValueOf(&cVal) 615 case complex64: 616 concreteVal = reflect.ValueOf(&cVal) 617 case complex128: 618 concreteVal = reflect.ValueOf(&cVal) 619 case uintptr: 620 concreteVal = reflect.ValueOf(&cVal) 621 default: 622 return ErrUnsupportedPtrType 623 } 624 625 if fieldValue.Type() != concreteVal.Type() { 626 return ErrUnsupportedPtrType 627 } 628 629 fieldValue.Set(concreteVal) 630 delete(data.Attributes, args[1]) 631 return nil 632 } 633 634 // As a final catch-all, ensure types line up to avoid a runtime panic. 635 // Ignore interfaces since interfaces are poly 636 if fieldValue.Kind() != reflect.Interface && fieldValue.Kind() != v.Kind() { 637 return ErrInvalidType 638 } 639 640 // set val and clear attribute key so its not processed again 641 fieldValue.Set(reflect.ValueOf(val)) 642 delete(data.Attributes, args[1]) 643 return nil 644 } 645 646 func fullNode(n *Node, included *map[string]*Node) *Node { 647 includedKey := fmt.Sprintf("%s,%s", n.Type, n.ID) 648 649 if included != nil && (*included)[includedKey] != nil { 650 return deepCopyNode((*included)[includedKey]) 651 } 652 653 return deepCopyNode(n) 654 } 655 656 // assign will take the value specified and assign it to the field; if 657 // field is expecting a ptr assign will assign a ptr. 658 func assign(field, value reflect.Value) { 659 if field.Kind() == reflect.Ptr { 660 field.Set(value) 661 } else { 662 field.Set(reflect.Indirect(value)) 663 } 664 }