jsonapi/response.go

155 lines
3.4 KiB
Go
Raw Normal View History

2015-07-05 11:59:30 -04:00
package jsonapi
import (
"errors"
"fmt"
"reflect"
"strings"
)
2015-07-06 15:06:12 -04:00
func MarshalJsonApiPayload(model interface{}) (*JsonApiPayload, error) {
2015-07-05 13:59:35 -04:00
rootNode, included, err := visitModelNode(model)
if err != nil {
return nil, err
}
resp := &JsonApiPayload{Data: rootNode}
uniqueIncluded := make(map[string]*JsonApiNode)
for i, n := range included {
k := fmt.Sprintf("%s,%s", n.Type, n.Id)
if uniqueIncluded[k] == nil {
uniqueIncluded[k] = n
} else {
included = append(included[:i], included[i+1:]...)
}
}
2015-07-05 13:59:35 -04:00
resp.Included = included
return resp, nil
}
2015-07-05 11:59:30 -04:00
2015-07-05 13:59:35 -04:00
func visitModelNode(model interface{}) (*JsonApiNode, []*JsonApiNode, error) {
node := new(JsonApiNode)
2015-07-05 11:59:30 -04:00
var err error
var included []*JsonApiNode
2015-07-05 13:59:35 -04:00
2015-07-06 13:38:42 -04:00
modelType := reflect.TypeOf(model).Elem()
modelValue := reflect.ValueOf(model).Elem()
2015-07-05 11:59:30 -04:00
2015-07-06 13:38:42 -04:00
var i = 0
2015-07-05 13:59:35 -04:00
modelType.FieldByNameFunc(func(name string) bool {
2015-07-06 13:38:42 -04:00
fieldType := modelType.Field(i)
fieldValue := modelValue.Field(i)
2015-07-05 11:59:30 -04:00
2015-07-06 13:38:42 -04:00
i += 1
tag := fieldType.Tag.Get("jsonapi")
2015-07-05 13:59:35 -04:00
2015-07-06 13:38:42 -04:00
args := strings.Split(tag, ",")
2015-07-05 13:59:35 -04:00
2015-07-06 13:38:42 -04:00
if len(args) >= 1 && args[0] != "" {
annotation := args[0]
2015-07-05 13:59:35 -04:00
2015-07-06 13:38:42 -04:00
if annotation == "primary" {
if len(args) >= 2 {
node.Id = fmt.Sprintf("%v", fieldValue.Interface())
node.Type = args[1]
} else {
err = errors.New("'type' as second argument required for 'primary'")
}
} else if annotation == "attr" {
if node.Attributes == nil {
node.Attributes = make(map[string]interface{})
}
if len(args) >= 2 {
node.Attributes[args[1]] = fieldValue.Interface()
} else {
err = errors.New("'type' as second argument required for 'primary'")
}
} else if annotation == "relation" {
2015-07-05 13:59:35 -04:00
2015-07-06 13:38:42 -04:00
isSlice := fieldValue.Type().Kind() == reflect.Slice
if (isSlice && fieldValue.Len() < 1) || (!isSlice && fieldValue.IsNil()) {
return false
}
if node.Relationships == nil {
node.Relationships = make(map[string]interface{})
}
if included == nil {
included = make([]*JsonApiNode, 0)
}
if isSlice {
relationship, err := visitModelNodeRelationships(args[1], fieldValue)
if err == nil {
shallowNodes := make([]*JsonApiNode, 0)
for k, v := range relationship {
for _, node := range v {
included = append(included, node)
shallowNode := *node
shallowNode.Attributes = nil
shallowNodes = append(shallowNodes, &shallowNode)
2015-07-05 13:59:35 -04:00
}
2015-07-06 13:38:42 -04:00
node.Relationships[k] = shallowNodes
2015-07-05 13:59:35 -04:00
}
} else {
2015-07-06 13:38:42 -04:00
err = err
}
} else {
relationship, _, err := visitModelNode(fieldValue.Interface())
if err == nil {
shallowNode := *relationship
shallowNode.Attributes = nil
2015-07-06 13:38:42 -04:00
included = append(included, relationship)
2015-07-06 13:38:42 -04:00
node.Relationships[args[1]] = &shallowNode
} else {
err = err
2015-07-05 13:59:35 -04:00
}
2015-07-05 11:59:30 -04:00
}
2015-07-06 13:38:42 -04:00
} else {
err = errors.New(fmt.Sprintf("Unsupported jsonapi tag annotation, %s", annotation))
2015-07-05 11:59:30 -04:00
}
}
return false
})
if err != nil {
2015-07-05 13:59:35 -04:00
return nil, nil, err
}
return node, included, nil
}
func visitModelNodeRelationships(relationName string, models reflect.Value) (map[string][]*JsonApiNode, error) {
relationship := make(map[string][]*JsonApiNode)
nodes := make([]*JsonApiNode, 0)
for i := 0; i < models.Len(); i++ {
node, _, err := visitModelNode(models.Index(i).Interface())
if err != nil {
return nil, err
}
nodes = append(nodes, node)
2015-07-05 11:59:30 -04:00
}
2015-07-05 13:59:35 -04:00
relationship[relationName] = nodes
return relationship, nil
2015-07-05 11:59:30 -04:00
}