forked from Mirrors/jsonapi
Merge pull request #9 from dustinsmith1024/master
Adding methods to return the payload vs writing it out.
This commit is contained in:
commit
7559c9fda0
91
response.go
91
response.go
|
@ -2,7 +2,6 @@ package jsonapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -10,7 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wrties a jsonapi response with one, with related records sideloaded, into "included" array.
|
// MarshalOnePayload writes a jsonapi response with one, with related records sideloaded, into "included" array.
|
||||||
// This method encodes a response for a single record only. Hence, data will be a single record rather
|
// This method encodes a response for a single record only. Hence, data will be a single record rather
|
||||||
// than an array of records. If you want to serialize many records, see, MarshalManyPayload.
|
// than an array of records. If you want to serialize many records, see, MarshalManyPayload.
|
||||||
//
|
//
|
||||||
|
@ -18,13 +17,10 @@ import (
|
||||||
//
|
//
|
||||||
// model interface{} should be a pointer to a struct.
|
// model interface{} should be a pointer to a struct.
|
||||||
func MarshalOnePayload(w io.Writer, model interface{}) error {
|
func MarshalOnePayload(w io.Writer, model interface{}) error {
|
||||||
rootNode, included, err := visitModelNode(model, true)
|
payload, err := MarshalOne(model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
payload := &OnePayload{Data: rootNode}
|
|
||||||
|
|
||||||
payload.Included = uniqueByTypeAndId(included)
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -33,7 +29,22 @@ func MarshalOnePayload(w io.Writer, model interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrties a jsonapi response with many records, with related records sideloaded, into "included" array.
|
// MarshalOne does the same as MarshalOnePayload except it just returns the payload
|
||||||
|
// and doesn't write out results.
|
||||||
|
// Useful is you use your JSON rendering library.
|
||||||
|
func MarshalOne(model interface{}) (*OnePayload, error) {
|
||||||
|
rootNode, included, err := visitModelNode(model, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload := &OnePayload{Data: rootNode}
|
||||||
|
|
||||||
|
payload.Included = uniqueByTypeAndID(included)
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalManyPayload writes a jsonapi response with many records, with related records sideloaded, into "included" array.
|
||||||
// This method encodes a response for a slice of records, hence data will be an array of
|
// This method encodes a response for a slice of records, hence data will be an array of
|
||||||
// records rather than a single record. To serialize a single record, see MarshalOnePayload
|
// records rather than a single record. To serialize a single record, see MarshalOnePayload
|
||||||
//
|
//
|
||||||
|
@ -57,26 +68,10 @@ func MarshalOnePayload(w io.Writer, model interface{}) error {
|
||||||
//
|
//
|
||||||
// models []interface{} should be a slice of struct pointers.
|
// models []interface{} should be a slice of struct pointers.
|
||||||
func MarshalManyPayload(w io.Writer, models []interface{}) error {
|
func MarshalManyPayload(w io.Writer, models []interface{}) error {
|
||||||
modelsValues := reflect.ValueOf(models)
|
payload, err := MarshalMany(models)
|
||||||
data := make([]*Node, 0, modelsValues.Len())
|
|
||||||
|
|
||||||
incl := make([]*Node, 0)
|
|
||||||
|
|
||||||
for i := 0; i < modelsValues.Len(); i += 1 {
|
|
||||||
model := modelsValues.Index(i).Interface()
|
|
||||||
|
|
||||||
node, included, err := visitModelNode(model, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data = append(data, node)
|
|
||||||
incl = append(incl, included...)
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := &ManyPayload{
|
|
||||||
Data: data,
|
|
||||||
Included: uniqueByTypeAndId(incl),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -85,7 +80,35 @@ func MarshalManyPayload(w io.Writer, models []interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method not meant to for use in implementation code, although feel
|
// MarshalMany does the same as MarshalManyPayload except it just returns the payload
|
||||||
|
// and doesn't write out results.
|
||||||
|
// Useful is you use your JSON rendering library.
|
||||||
|
func MarshalMany(models []interface{}) (*ManyPayload, error) {
|
||||||
|
modelsValues := reflect.ValueOf(models)
|
||||||
|
data := make([]*Node, 0, modelsValues.Len())
|
||||||
|
|
||||||
|
var incl []*Node
|
||||||
|
|
||||||
|
for i := 0; i < modelsValues.Len(); i++ {
|
||||||
|
model := modelsValues.Index(i).Interface()
|
||||||
|
|
||||||
|
node, included, err := visitModelNode(model, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data = append(data, node)
|
||||||
|
incl = append(incl, included...)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := &ManyPayload{
|
||||||
|
Data: data,
|
||||||
|
Included: uniqueByTypeAndID(incl),
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalOnePayloadEmbedded - This method not meant to for use in implementation code, although feel
|
||||||
// free. The purpose of this method is for use in tests. In most cases, your
|
// free. The purpose of this method is for use in tests. In most cases, your
|
||||||
// request payloads for create will be embedded rather than sideloaded for related records.
|
// request payloads for create will be embedded rather than sideloaded for related records.
|
||||||
// This method will serialize a single struct pointer into an embedded json
|
// This method will serialize a single struct pointer into an embedded json
|
||||||
|
@ -130,18 +153,18 @@ func visitModelNode(model interface{}, sideload bool) (*Node, []*Node, error) {
|
||||||
structField := modelType.Field(i)
|
structField := modelType.Field(i)
|
||||||
tag := structField.Tag.Get("jsonapi")
|
tag := structField.Tag.Get("jsonapi")
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
i += 1
|
i++
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldValue := modelValue.Field(i)
|
fieldValue := modelValue.Field(i)
|
||||||
|
|
||||||
i += 1
|
i++
|
||||||
|
|
||||||
args := strings.Split(tag, ",")
|
args := strings.Split(tag, ",")
|
||||||
|
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
er = errors.New(fmt.Sprintf("jsonapi tag, on %s, had two few arguments", structField.Name))
|
er = fmt.Errorf("jsonapi tag, on %s, had two few arguments", structField.Name)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +214,7 @@ func visitModelNode(model interface{}, sideload bool) (*Node, []*Node, error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if sideload {
|
if sideload {
|
||||||
included = append(included, incl...)
|
included = append(included, incl...)
|
||||||
shallowNodes := make([]*Node, 0)
|
var shallowNodes []*Node
|
||||||
for _, node := range d {
|
for _, node := range d {
|
||||||
shallowNodes = append(shallowNodes, toShallowNode(node))
|
shallowNodes = append(shallowNodes, toShallowNode(node))
|
||||||
}
|
}
|
||||||
|
@ -221,7 +244,7 @@ func visitModelNode(model interface{}, sideload bool) (*Node, []*Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
er = errors.New(fmt.Sprintf("Unsupported jsonapi tag annotation, %s", annotation))
|
er = fmt.Errorf("Unsupported jsonapi tag annotation, %s", annotation)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,9 +267,9 @@ func toShallowNode(node *Node) *Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
func visitModelNodeRelationships(relationName string, models reflect.Value, sideload bool) (*RelationshipManyNode, []*Node, error) {
|
func visitModelNodeRelationships(relationName string, models reflect.Value, sideload bool) (*RelationshipManyNode, []*Node, error) {
|
||||||
nodes := make([]*Node, 0)
|
var nodes []*Node
|
||||||
|
|
||||||
var included []*Node
|
var included []*Node
|
||||||
|
|
||||||
if sideload {
|
if sideload {
|
||||||
included = make([]*Node, 0)
|
included = make([]*Node, 0)
|
||||||
}
|
}
|
||||||
|
@ -268,10 +291,10 @@ func visitModelNodeRelationships(relationName string, models reflect.Value, side
|
||||||
return n, included, nil
|
return n, included, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func uniqueByTypeAndId(nodes []*Node) []*Node {
|
func uniqueByTypeAndID(nodes []*Node) []*Node {
|
||||||
uniqueIncluded := make(map[string]*Node)
|
uniqueIncluded := make(map[string]*Node)
|
||||||
|
|
||||||
for i := 0; i < len(nodes); i += 1 {
|
for i := 0; i < len(nodes); i++ {
|
||||||
n := nodes[i]
|
n := nodes[i]
|
||||||
k := fmt.Sprintf("%s,%s", n.Type, n.Id)
|
k := fmt.Sprintf("%s,%s", n.Type, n.Id)
|
||||||
if uniqueIncluded[k] == nil {
|
if uniqueIncluded[k] == nil {
|
||||||
|
|
Loading…
Reference in New Issue