2015-07-05 11:59:30 -04:00
|
|
|
package jsonapi
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type JsonApiResponse struct {
|
|
|
|
Data *JsonApiNode `json:"data"`
|
2015-07-05 15:12:09 -04:00
|
|
|
Included []*JsonApiNode `json:"included,omitempty"`
|
2015-07-05 11:59:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func CreateJsonApiResponse(model interface{}) (*JsonApiResponse, error) {
|
2015-07-05 13:59:35 -04:00
|
|
|
rootNode, included, err := visitModelNode(model)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := &JsonApiResponse{Data: rootNode}
|
2015-07-05 15:12:09 -04:00
|
|
|
|
|
|
|
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
|
2015-07-06 12:24:35 -04:00
|
|
|
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-05 15:12:09 -04:00
|
|
|
|
2015-07-06 13:38:42 -04:00
|
|
|
included = append(included, relationship)
|
2015-07-05 15:12:09 -04:00
|
|
|
|
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
|
|
|
}
|