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  }