jsonapi/response.go

247 lines
5.2 KiB
Go
Raw Normal View History

2015-07-05 11:59:30 -04:00
package jsonapi
import (
"encoding/json"
2015-07-05 11:59:30 -04:00
"errors"
"fmt"
"io"
2015-07-05 11:59:30 -04:00
"reflect"
"strings"
"time"
2015-07-05 11:59:30 -04:00
)
2015-07-10 20:16:26 -04:00
func MarshalOnePayload(w io.Writer, model interface{}) error {
rootNode, included, err := visitModelNode(model, true)
if err != nil {
return err
}
payload := &OnePayload{Data: rootNode}
2015-07-12 10:59:37 -04:00
payload.Included = uniqueByTypeAndId(included)
2015-07-10 20:16:26 -04:00
if err := json.NewEncoder(w).Encode(payload); err != nil {
return err
}
return nil
}
func MarshalManyPayload(w io.Writer, models Models) error {
2015-07-07 12:52:38 -04:00
d := models.GetData()
data := make([]*Node, 0, len(d))
2015-07-07 12:52:38 -04:00
incl := make([]*Node, 0)
2015-07-07 12:52:38 -04:00
for _, model := range d {
node, included, err := visitModelNode(model, true)
2015-07-07 12:52:38 -04:00
if err != nil {
return err
2015-07-07 12:52:38 -04:00
}
data = append(data, node)
incl = append(incl, included...)
}
payload := &ManyPayload{
2015-07-07 12:52:38 -04:00
Data: data,
2015-07-12 10:59:37 -04:00
Included: uniqueByTypeAndId(incl),
}
if err := json.NewEncoder(w).Encode(payload); err != nil {
return err
}
return nil
2015-07-07 12:52:38 -04:00
}
func MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error {
rootNode, _, err := visitModelNode(model, false)
if err != nil {
return err
}
payload := &OnePayload{Data: rootNode}
if err := json.NewEncoder(w).Encode(payload); err != nil {
return err
}
return nil
}
func visitModelNode(model interface{}, sideload bool) (*Node, []*Node, error) {
node := new(Node)
2015-07-05 11:59:30 -04:00
2015-07-06 17:39:24 -04:00
var er error
var included []*Node
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
fieldValue := modelValue.Field(i)
structField := modelType.Field(i)
2015-07-05 11:59:30 -04:00
2015-07-06 13:38:42 -04:00
i += 1
tag := structField.Tag.Get("jsonapi")
2015-07-05 13:59:35 -04:00
2015-07-08 16:11:03 -04:00
if tag == "" {
return false
}
2015-07-06 13:38:42 -04:00
args := strings.Split(tag, ",")
2015-07-05 13:59:35 -04:00
if len(args) != 2 {
er = errors.New(fmt.Sprintf("jsonapi tag, on %s, had two few arguments", structField.Name))
return false
}
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" {
node.Id = fmt.Sprintf("%v", fieldValue.Interface())
node.Type = args[1]
2015-07-06 13:38:42 -04:00
} else if annotation == "attr" {
if node.Attributes == nil {
node.Attributes = make(map[string]interface{})
}
if fieldValue.Type() == reflect.TypeOf(time.Time{}) {
isZeroMethod := fieldValue.MethodByName("IsZero")
isZero := isZeroMethod.Call(make([]reflect.Value, 0))[0].Interface().(bool)
if isZero {
return false
}
unix := fieldValue.MethodByName("Unix")
val := unix.Call(make([]reflect.Value, 0))[0]
node.Attributes[args[1]] = val.Int()
2015-07-06 13:38:42 -04:00
} else {
node.Attributes[args[1]] = fieldValue.Interface()
2015-07-06 13:38:42 -04:00
}
} else if annotation == "relation" {
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 sideload && included == nil {
included = make([]*Node, 0)
2015-07-06 13:38:42 -04:00
}
if isSlice {
relationship, incl, err := visitModelNodeRelationships(args[1], fieldValue, sideload)
d := relationship.Data
2015-07-06 13:38:42 -04:00
if err == nil {
if sideload {
included = append(included, incl...)
shallowNodes := make([]*Node, 0)
for _, node := range d {
shallowNodes = append(shallowNodes, toShallowNode(node))
}
node.Relationships[args[1]] = &RelationshipManyNode{Data: shallowNodes}
} else {
node.Relationships[args[1]] = relationship
}
2015-07-05 13:59:35 -04:00
} else {
2015-07-06 17:39:24 -04:00
er = err
return false
2015-07-06 13:38:42 -04:00
}
} else {
relationship, incl, err := visitModelNode(fieldValue.Interface(), sideload)
2015-07-06 13:38:42 -04:00
if err == nil {
if sideload {
included = append(included, incl...)
included = append(included, relationship)
node.Relationships[args[1]] = &RelationshipOneNode{Data: toShallowNode(relationship)}
} else {
node.Relationships[args[1]] = &RelationshipOneNode{Data: relationship}
}
2015-07-06 13:38:42 -04:00
} else {
2015-07-06 17:39:24 -04:00
er = err
return false
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 {
2015-07-06 17:39:24 -04:00
er = errors.New(fmt.Sprintf("Unsupported jsonapi tag annotation, %s", annotation))
return false
2015-07-05 11:59:30 -04:00
}
}
return false
})
2015-07-06 17:39:24 -04:00
if er != nil {
return nil, nil, er
2015-07-05 13:59:35 -04:00
}
return node, included, nil
}
func toShallowNode(node *Node) *Node {
return &Node{
Id: node.Id,
Type: node.Type,
}
2015-07-10 11:25:24 -04:00
}
func visitModelNodeRelationships(relationName string, models reflect.Value, sideload bool) (*RelationshipManyNode, []*Node, error) {
nodes := make([]*Node, 0)
2015-07-05 13:59:35 -04:00
var included []*Node
if sideload {
included = make([]*Node, 0)
}
2015-07-05 13:59:35 -04:00
for i := 0; i < models.Len(); i++ {
node, incl, err := visitModelNode(models.Index(i).Interface(), sideload)
2015-07-05 13:59:35 -04:00
if err != nil {
return nil, nil, err
2015-07-05 13:59:35 -04:00
}
nodes = append(nodes, node)
included = append(included, incl...)
2015-07-05 11:59:30 -04:00
}
included = append(included, nodes...)
n := &RelationshipManyNode{Data: nodes}
2015-07-05 13:59:35 -04:00
return n, included, nil
2015-07-05 11:59:30 -04:00
}
2015-07-07 12:52:38 -04:00
2015-07-12 10:59:37 -04:00
func uniqueByTypeAndId(nodes []*Node) []*Node {
uniqueIncluded := make(map[string]*Node)
for i, n := range nodes {
k := fmt.Sprintf("%s,%s", n.Type, n.Id)
if uniqueIncluded[k] == nil {
uniqueIncluded[k] = n
} else {
nodes = deleteNode(nodes, i)
}
}
return nodes
}
func deleteNode(a []*Node, i int) []*Node {
2015-07-07 12:52:38 -04:00
if i < len(a)-1 {
a = append(a[:i], a[i+1:]...)
} else {
a = a[:i]
}
return a
}