forked from Mirrors/jsonapi
Public Marshal API Simplification (#90)
* Used to have 6 functions: MarshalOnePayload, MarshalOnePayloadWithoutIncluded, MarshalOne, MarshalManyPayload, MarshalManyPayloadWithoutIncluded, MarshalMany - now there are only 3: MarshalPayload, MarshalPayloadWithoutIncluded, Marshal. This simplifies the public API; will be easier to explain and document while still providing the existing support for struct ptrs (&Blog{}) and slices of struct ptrs ([]*Blog{}). * Correcting all code occurrences leftover. * Updating README to reflect Marshal API changes. * Omit the mention of One vs Many
This commit is contained in:
parent
ebb7923313
commit
cf83b97b3d
71
README.md
71
README.md
|
@ -206,18 +206,17 @@ UnmarshalPayload(in io.Reader, model interface{})
|
||||||
|
|
||||||
Visit [godoc](http://godoc.org/github.com/google/jsonapi#UnmarshalPayload)
|
Visit [godoc](http://godoc.org/github.com/google/jsonapi#UnmarshalPayload)
|
||||||
|
|
||||||
#### `MarshalOnePayload`
|
#### `MarshalPayload`
|
||||||
|
|
||||||
```go
|
```go
|
||||||
MarshalOnePayload(w io.Writer, model interface{}) error
|
MarshalPayload(w io.Writer, models interface{}) error
|
||||||
```
|
```
|
||||||
|
|
||||||
Visit [godoc](http://godoc.org/github.com/google/jsonapi#MarshalOnePayload)
|
Visit [godoc](http://godoc.org/github.com/google/jsonapi#MarshalPayload)
|
||||||
|
|
||||||
Writes a JSON API response, with related records sideloaded, into an
|
Writes a JSON API response, with related records sideloaded, into an
|
||||||
`included` array. This method encodes a response for a single record
|
`included` array. This method encodes a response for either a single record or
|
||||||
only. If you want to serialize many records, see,
|
many records.
|
||||||
[MarshalManyPayload](#marshalmanypayload).
|
|
||||||
|
|
||||||
##### Handler Example Code
|
##### Handler Example Code
|
||||||
|
|
||||||
|
@ -235,63 +234,7 @@ func CreateBlog(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", jsonapi.MediaType)
|
w.Header().Set("Content-Type", jsonapi.MediaType)
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
|
||||||
if err := jsonapi.MarshalOnePayload(w, blog); err != nil {
|
if err := jsonapi.MarshalPayload(w, blog); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### List Records Example
|
|
||||||
|
|
||||||
#### `MarshalManyPayload`
|
|
||||||
|
|
||||||
```go
|
|
||||||
MarshalManyPayload(w io.Writer, models []interface{}) error
|
|
||||||
```
|
|
||||||
|
|
||||||
Visit [godoc](http://godoc.org/github.com/google/jsonapi#MarshalManyPayload)
|
|
||||||
|
|
||||||
Takes an `io.Writer` and an slice of `interface{}`. Note, if you have a
|
|
||||||
type safe array of your structs, like,
|
|
||||||
|
|
||||||
```go
|
|
||||||
var blogs []*Blog
|
|
||||||
```
|
|
||||||
|
|
||||||
you will need to iterate over the slice of `Blog` pointers and append
|
|
||||||
them to an interface array, like,
|
|
||||||
|
|
||||||
```go
|
|
||||||
blogInterface := make([]interface{}, len(blogs))
|
|
||||||
|
|
||||||
for i, blog := range blogs {
|
|
||||||
blogInterface[i] = blog
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, you can insert your `Blog`s into a slice of `interface{}`
|
|
||||||
the first time. For example when you fetch the `Blog`s from the db
|
|
||||||
`append` them to an `[]interface{}` rather than a `[]*Blog`. So your
|
|
||||||
method signature to reach into your data store may look something like
|
|
||||||
this,
|
|
||||||
|
|
||||||
```go
|
|
||||||
func FetchBlogs() ([]interface{}, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Handler Example Code
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ListBlogs(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// ...fetch your blogs, filter, offset, limit, etc...
|
|
||||||
|
|
||||||
// but, for now
|
|
||||||
blogs := testBlogsForList()
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", jsonapi.MediaType)
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
|
|
||||||
if err := jsonapi.MarshalManyPayload(w, blogs); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,7 +272,7 @@ func CreateBlogs(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", jsonapi.MediaType)
|
w.Header().Set("Content-Type", jsonapi.MediaType)
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
|
||||||
if err := jsonapi.MarshalManyPayload(w, blogs); err != nil {
|
if err := jsonapi.MarshalPayload(w, blogs); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ func exerciseHandler() {
|
||||||
fixtureBlogCreate(3),
|
fixtureBlogCreate(3),
|
||||||
}
|
}
|
||||||
in = bytes.NewBuffer(nil)
|
in = bytes.NewBuffer(nil)
|
||||||
jsonapi.MarshalManyPayload(in, blogs)
|
jsonapi.MarshalPayload(in, blogs)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodPut, "/blogs", in)
|
req, _ = http.NewRequest(http.MethodPut, "/blogs", in)
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ func (h *ExampleHandler) createBlog(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
w.Header().Set(headerContentType, jsonapi.MediaType)
|
w.Header().Set(headerContentType, jsonapi.MediaType)
|
||||||
|
|
||||||
if err := jsonapiRuntime.MarshalOnePayload(w, blog); err != nil {
|
if err := jsonapiRuntime.MarshalPayload(w, blog); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ func (h *ExampleHandler) echoBlogs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Header().Set(headerContentType, jsonapi.MediaType)
|
w.Header().Set(headerContentType, jsonapi.MediaType)
|
||||||
if err := jsonapiRuntime.MarshalManyPayload(w, blogs); err != nil {
|
if err := jsonapiRuntime.MarshalPayload(w, blogs); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func (h *ExampleHandler) showBlog(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
w.Header().Set(headerContentType, jsonapi.MediaType)
|
w.Header().Set(headerContentType, jsonapi.MediaType)
|
||||||
if err := jsonapiRuntime.MarshalOnePayload(w, blog); err != nil {
|
if err := jsonapiRuntime.MarshalPayload(w, blog); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ func (h *ExampleHandler) listBlogs(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", jsonapi.MediaType)
|
w.Header().Set("Content-Type", jsonapi.MediaType)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
if err := jsonapiRuntime.MarshalManyPayload(w, blogs); err != nil {
|
if err := jsonapiRuntime.MarshalPayload(w, blogs); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestExampleHandler_put(t *testing.T) {
|
||||||
fixtureBlogCreate(3),
|
fixtureBlogCreate(3),
|
||||||
}
|
}
|
||||||
requestBody := bytes.NewBuffer(nil)
|
requestBody := bytes.NewBuffer(nil)
|
||||||
jsonapi.MarshalManyPayload(requestBody, blogs)
|
jsonapi.MarshalPayload(requestBody, blogs)
|
||||||
|
|
||||||
r, err := http.NewRequest(http.MethodPut, "/blogs", requestBody)
|
r, err := http.NewRequest(http.MethodPut, "/blogs", requestBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
13
node.go
13
node.go
|
@ -2,6 +2,11 @@ package jsonapi
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
|
// Payloader is used to encapsulate the One and Many payload types
|
||||||
|
type Payloader interface {
|
||||||
|
clearIncluded()
|
||||||
|
}
|
||||||
|
|
||||||
// OnePayload is used to represent a generic JSON API payload where a single
|
// OnePayload is used to represent a generic JSON API payload where a single
|
||||||
// resource (Node) was included as an {} in the "data" key
|
// resource (Node) was included as an {} in the "data" key
|
||||||
type OnePayload struct {
|
type OnePayload struct {
|
||||||
|
@ -11,6 +16,10 @@ type OnePayload struct {
|
||||||
Meta *Meta `json:"meta,omitempty"`
|
Meta *Meta `json:"meta,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *OnePayload) clearIncluded() {
|
||||||
|
p.Included = []*Node{}
|
||||||
|
}
|
||||||
|
|
||||||
// ManyPayload is used to represent a generic JSON API payload where many
|
// ManyPayload is used to represent a generic JSON API payload where many
|
||||||
// resources (Nodes) were included in an [] in the "data" key
|
// resources (Nodes) were included in an [] in the "data" key
|
||||||
type ManyPayload struct {
|
type ManyPayload struct {
|
||||||
|
@ -20,6 +29,10 @@ type ManyPayload struct {
|
||||||
Meta *Meta `json:"meta,omitempty"`
|
Meta *Meta `json:"meta,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ManyPayload) clearIncluded() {
|
||||||
|
p.Included = []*Node{}
|
||||||
|
}
|
||||||
|
|
||||||
// Node is used to represent a generic JSON API Resource
|
// Node is used to represent a generic JSON API Resource
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
|
|
@ -58,7 +58,7 @@ var (
|
||||||
// w.Header().Set("Content-Type", jsonapi.MediaType)
|
// w.Header().Set("Content-Type", jsonapi.MediaType)
|
||||||
// w.WriteHeader(201)
|
// w.WriteHeader(201)
|
||||||
//
|
//
|
||||||
// if err := jsonapi.MarshalOnePayload(w, blog); err != nil {
|
// if err := jsonapi.MarshalPayload(w, blog); err != nil {
|
||||||
// http.Error(w, err.Error(), 500)
|
// http.Error(w, err.Error(), 500)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -931,7 +931,7 @@ func samplePayloadWithSideloaded() io.Reader {
|
||||||
testModel := testModel()
|
testModel := testModel()
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
MarshalOnePayload(out, testModel)
|
MarshalPayload(out, testModel)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
182
response.go
182
response.go
|
@ -23,18 +23,47 @@ var (
|
||||||
// be a slice of *Structs; MarshalMany will return this error when its
|
// be a slice of *Structs; MarshalMany will return this error when its
|
||||||
// interface{} argument is invalid.
|
// interface{} argument is invalid.
|
||||||
ErrExpectedSlice = errors.New("models should be a slice of struct pointers")
|
ErrExpectedSlice = errors.New("models should be a slice of struct pointers")
|
||||||
|
// ErrUnexpectedType is returned when marshalling an interface; the interface
|
||||||
|
// had to be a pointer or a slice; otherwise this error is returned.
|
||||||
|
ErrUnexpectedType = errors.New("models should be a struct pointer or slice of struct pointers")
|
||||||
)
|
)
|
||||||
|
|
||||||
// MarshalOnePayload writes a jsonapi response with one, with related records
|
// MarshalPayload writes a jsonapi response for one or many records. The
|
||||||
// sideloaded, into "included" array. This method encodes a response for a
|
// related records are sideloaded into the "included" array. If this method is
|
||||||
// single record only. Hence, data will be a single record rather than an array
|
// given a struct pointer as an argument it will serialize in the form
|
||||||
// of records. If you want to serialize many records, see, MarshalManyPayload.
|
// "data": {...}. If this method is given a slice of pointers, this method will
|
||||||
|
// serialize in the form "data": [...]
|
||||||
//
|
//
|
||||||
// See UnmarshalPayload for usage example.
|
// One Example: you could pass it, w, your http.ResponseWriter, and, models, a
|
||||||
|
// ptr to a Blog to be written to the response body:
|
||||||
//
|
//
|
||||||
// model interface{} should be a pointer to a struct.
|
// func ShowBlog(w http.ResponseWriter, r *http.Request) {
|
||||||
func MarshalOnePayload(w io.Writer, model interface{}) error {
|
// blog := &Blog{}
|
||||||
payload, err := MarshalOne(model)
|
//
|
||||||
|
// w.Header().Set("Content-Type", jsonapi.MediaType)
|
||||||
|
// w.WriteHeader(http.StatusOK)
|
||||||
|
//
|
||||||
|
// if err := jsonapi.MarshalPayload(w, blog); err != nil {
|
||||||
|
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Many Example: you could pass it, w, your http.ResponseWriter, and, models, a
|
||||||
|
// slice of Blog struct instance pointers to be written to the response body:
|
||||||
|
//
|
||||||
|
// func ListBlogs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// blogs := []*Blog{}
|
||||||
|
//
|
||||||
|
// w.Header().Set("Content-Type", jsonapi.MediaType)
|
||||||
|
// w.WriteHeader(http.StatusOK)
|
||||||
|
//
|
||||||
|
// if err := jsonapi.MarshalPayload(w, blogs); err != nil {
|
||||||
|
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func MarshalPayload(w io.Writer, models interface{}) error {
|
||||||
|
payload, err := Marshal(models)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -42,34 +71,55 @@ func MarshalOnePayload(w io.Writer, model interface{}) error {
|
||||||
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalOnePayloadWithoutIncluded writes a jsonapi response with one object,
|
// Marshal does the same as MarshalPayload except it just returns the payload
|
||||||
// without the related records sideloaded into "included" array. If you want to
|
// and doesn't write out results. Useful if you use your own JSON rendering
|
||||||
// serialize the relations into the "included" array see MarshalOnePayload.
|
// library.
|
||||||
//
|
func Marshal(models interface{}) (Payloader, error) {
|
||||||
// model interface{} should be a pointer to a struct.
|
switch vals := reflect.ValueOf(models); vals.Kind() {
|
||||||
func MarshalOnePayloadWithoutIncluded(w io.Writer, model interface{}) error {
|
case reflect.Slice:
|
||||||
included := make(map[string]*Node)
|
m, err := convertToSliceInterface(&models)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return marshalMany(m)
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Check that the pointer was to a struct
|
||||||
|
if reflect.Indirect(vals).Kind() != reflect.Struct {
|
||||||
|
return nil, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
return marshalOne(models)
|
||||||
|
default:
|
||||||
|
return nil, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rootNode, err := visitModelNode(model, &included, true)
|
// MarshalPayloadWithoutIncluded writes a jsonapi response with one or many
|
||||||
|
// records, without the related records sideloaded into "included" array.
|
||||||
|
// If you want to serialize the relations into the "included" array see
|
||||||
|
// MarshalPayload.
|
||||||
|
//
|
||||||
|
// models interface{} should be either a struct pointer or a slice of struct
|
||||||
|
// pointers.
|
||||||
|
func MarshalPayloadWithoutIncluded(w io.Writer, model interface{}) error {
|
||||||
|
payload, err := Marshal(model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
payload.clearIncluded()
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(&OnePayload{Data: rootNode}); err != nil {
|
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalOne does the same as MarshalOnePayload except it just returns the
|
// 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
|
// payload and doesn't write out results. Useful is you use your JSON rendering
|
||||||
// library.
|
// library.
|
||||||
func MarshalOne(model interface{}) (*OnePayload, error) {
|
func marshalOne(model interface{}) (*OnePayload, error) {
|
||||||
included := make(map[string]*Node)
|
included := make(map[string]*Node)
|
||||||
|
|
||||||
rootNode, err := visitModelNode(model, &included, true)
|
rootNode, err := visitModelNode(model, &included, true)
|
||||||
|
@ -83,78 +133,10 @@ func MarshalOne(model interface{}) (*OnePayload, error) {
|
||||||
return payload, nil
|
return payload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalManyPayloadWithoutIncluded writes a jsonapi response with many records,
|
// marshalMany does the same as MarshalManyPayload except it just returns the
|
||||||
// without the related records sideloaded into "included" array. If you want to
|
|
||||||
// serialize the relations into the "included" array see MarshalManyPayload.
|
|
||||||
//
|
|
||||||
// models interface{} should be a slice of struct pointers.
|
|
||||||
func MarshalManyPayloadWithoutIncluded(w io.Writer, models interface{}) error {
|
|
||||||
m, err := convertToSliceInterface(&models)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
payload, err := MarshalMany(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty the included
|
|
||||||
payload.Included = []*Node{}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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 records rather than a
|
|
||||||
// single record. To serialize a single record, see MarshalOnePayload
|
|
||||||
//
|
|
||||||
// For example you could pass it, w, your http.ResponseWriter, and, models, a
|
|
||||||
// slice of Blog struct instance pointers as interface{}'s to write to the
|
|
||||||
// response,
|
|
||||||
//
|
|
||||||
// func ListBlogs(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// // ... fetch your blogs and filter, offset, limit, etc ...
|
|
||||||
//
|
|
||||||
// blogs := testBlogsForList()
|
|
||||||
//
|
|
||||||
// w.Header().Set("Content-Type", jsonapi.MediaType)
|
|
||||||
// w.WriteHeader(http.StatusOK)
|
|
||||||
//
|
|
||||||
// if err := jsonapi.MarshalManyPayload(w, blogs); err != nil {
|
|
||||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Visit https://github.com/google/jsonapi#list for more info.
|
|
||||||
//
|
|
||||||
// models interface{} should be a slice of struct pointers.
|
|
||||||
func MarshalManyPayload(w io.Writer, models interface{}) error {
|
|
||||||
m, err := convertToSliceInterface(&models)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
payload, err := MarshalMany(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// payload and doesn't write out results. Useful is you use your JSON rendering
|
||||||
// library.
|
// library.
|
||||||
func MarshalMany(models []interface{}) (*ManyPayload, error) {
|
func marshalMany(models []interface{}) (*ManyPayload, error) {
|
||||||
payload := &ManyPayload{
|
payload := &ManyPayload{
|
||||||
Data: []*Node{},
|
Data: []*Node{},
|
||||||
}
|
}
|
||||||
|
@ -173,16 +155,18 @@ func MarshalMany(models []interface{}) (*ManyPayload, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalOnePayloadEmbedded - This method not meant to for use in
|
// MarshalOnePayloadEmbedded - This method not meant to for use in
|
||||||
// implementation code, although feel free. The purpose of this method is for
|
// implementation code, although feel free. The purpose of this
|
||||||
// use in tests. In most cases, your request payloads for create will be
|
// method is for use in tests. In most cases, your request
|
||||||
// embedded rather than sideloaded for related records. This method will
|
// payloads for create will be embedded rather than sideloaded for
|
||||||
// serialize a single struct pointer into an embedded json response. In other
|
// related records. This method will serialize a single struct
|
||||||
// words, there will be no, "included", array in the json all relationships will
|
// pointer into an embedded json response. In other words, there
|
||||||
|
// will be no, "included", array in the json all relationships will
|
||||||
// be serailized inline in the data.
|
// be serailized inline in the data.
|
||||||
//
|
//
|
||||||
// However, in tests, you may want to construct payloads to post to create
|
// However, in tests, you may want to construct payloads to post
|
||||||
// methods that are embedded to most closely resemble the payloads that will be
|
// to create methods that are embedded to most closely resemble
|
||||||
// produced by the client. This is what this method is intended for.
|
// the payloads that will be produced by the client. This is what
|
||||||
|
// this method is intended for.
|
||||||
//
|
//
|
||||||
// model interface{} should be a pointer to a struct.
|
// model interface{} should be a pointer to a struct.
|
||||||
func MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error {
|
func MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error {
|
||||||
|
|
|
@ -9,12 +9,40 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMarshall_attrStringSlice(t *testing.T) {
|
func TestMarshalPayload(t *testing.T) {
|
||||||
|
book := &Book{ID: 1}
|
||||||
|
books := []*Book{book, &Book{ID: 2}}
|
||||||
|
var jsonData map[string]interface{}
|
||||||
|
|
||||||
|
// One
|
||||||
|
out1 := bytes.NewBuffer(nil)
|
||||||
|
MarshalPayload(out1, book)
|
||||||
|
|
||||||
|
if err := json.Unmarshal(out1.Bytes(), &jsonData); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, ok := jsonData["data"].(map[string]interface{}); !ok {
|
||||||
|
t.Fatalf("data key did not contain an Hash/Dict/Map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Many
|
||||||
|
out2 := bytes.NewBuffer(nil)
|
||||||
|
MarshalPayload(out2, books)
|
||||||
|
|
||||||
|
if err := json.Unmarshal(out2.Bytes(), &jsonData); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, ok := jsonData["data"].([]interface{}); !ok {
|
||||||
|
t.Fatalf("data key did not contain an Array")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshal_attrStringSlice(t *testing.T) {
|
||||||
tags := []string{"fiction", "sale"}
|
tags := []string{"fiction", "sale"}
|
||||||
b := &Book{ID: 1, Tags: tags}
|
b := &Book{ID: 1, Tags: tags}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, b); err != nil {
|
if err := MarshalPayload(out, b); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +77,7 @@ func TestWithoutOmitsEmptyAnnotationOnRelation(t *testing.T) {
|
||||||
blog := &Blog{}
|
blog := &Blog{}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, blog); err != nil {
|
if err := MarshalPayload(out, blog); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +137,7 @@ func TestWithOmitsEmptyAnnotationOnRelation(t *testing.T) {
|
||||||
blog := &BlogOptionalPosts{ID: 999}
|
blog := &BlogOptionalPosts{ID: 999}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, blog); err != nil {
|
if err := MarshalPayload(out, blog); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +169,7 @@ func TestWithOmitsEmptyAnnotationOnRelation_MixedData(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, blog); err != nil {
|
if err := MarshalPayload(out, blog); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +203,7 @@ func TestMarshalIDPtr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, car); err != nil {
|
if err := MarshalPayload(out, car); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +232,7 @@ func TestMarshalOnePayload_omitIDString(t *testing.T) {
|
||||||
|
|
||||||
foo := &Foo{Title: "Foo"}
|
foo := &Foo{Title: "Foo"}
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, foo); err != nil {
|
if err := MarshalPayload(out, foo); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +258,7 @@ func TestMarshall_invalidIDType(t *testing.T) {
|
||||||
o := &badIDStruct{ID: &id}
|
o := &badIDStruct{ID: &id}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, o); err != ErrBadJSONAPIID {
|
if err := MarshalPayload(out, o); err != ErrBadJSONAPIID {
|
||||||
t.Fatalf(
|
t.Fatalf(
|
||||||
"Was expecting a `%s` error, got `%s`", ErrBadJSONAPIID, err,
|
"Was expecting a `%s` error, got `%s`", ErrBadJSONAPIID, err,
|
||||||
)
|
)
|
||||||
|
@ -244,7 +272,7 @@ func TestOmitsEmptyAnnotation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, book); err != nil {
|
if err := MarshalPayload(out, book); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +309,7 @@ func TestHasPrimaryAnnotation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +338,7 @@ func TestSupportsAttributes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +366,7 @@ func TestOmitsZeroTimes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +393,7 @@ func TestMarshalISO8601Time(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +421,7 @@ func TestMarshalISO8601TimePointer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +449,7 @@ func TestSupportsLinkable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +518,7 @@ func TestInvalidLinkable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err == nil {
|
if err := MarshalPayload(out, testModel); err == nil {
|
||||||
t.Fatal("Was expecting an error")
|
t.Fatal("Was expecting an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,7 +531,7 @@ func TestSupportsMetable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +555,7 @@ func TestRelations(t *testing.T) {
|
||||||
testModel := testBlog()
|
testModel := testBlog()
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,7 +601,7 @@ func TestNoRelations(t *testing.T) {
|
||||||
testModel := &Blog{ID: 1, Title: "Title 1", CreatedAt: time.Now()}
|
testModel := &Blog{ID: 1, Title: "Title 1", CreatedAt: time.Now()}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayload(out, testModel); err != nil {
|
if err := MarshalPayload(out, testModel); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,7 +615,7 @@ func TestNoRelations(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalOnePayloadWithoutIncluded(t *testing.T) {
|
func TestMarshalPayloadWithoutIncluded(t *testing.T) {
|
||||||
data := &Post{
|
data := &Post{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
BlogID: 2,
|
BlogID: 2,
|
||||||
|
@ -611,7 +639,7 @@ func TestMarshalOnePayloadWithoutIncluded(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalOnePayloadWithoutIncluded(out, data); err != nil {
|
if err := MarshalPayloadWithoutIncluded(out, data); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,7 +653,7 @@ func TestMarshalOnePayloadWithoutIncluded(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalMany(t *testing.T) {
|
func TestMarshalPayload_many(t *testing.T) {
|
||||||
data := []interface{}{
|
data := []interface{}{
|
||||||
&Blog{
|
&Blog{
|
||||||
ID: 5,
|
ID: 5,
|
||||||
|
@ -674,7 +702,7 @@ func TestMarshalMany(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalManyPayload(out, data); err != nil {
|
if err := MarshalPayload(out, data); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,7 +725,7 @@ func TestMarshalMany_WithSliceOfStructPointers(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalManyPayload(out, data); err != nil {
|
if err := MarshalPayload(out, data); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,7 +748,7 @@ func TestMarshalManyWithoutIncluded(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
if err := MarshalManyPayloadWithoutIncluded(out, data); err != nil {
|
if err := MarshalPayloadWithoutIncluded(out, data); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,11 +780,11 @@ func TestMarshalMany_SliceOfInterfaceAndSliceOfStructsSameJSON(t *testing.T) {
|
||||||
|
|
||||||
// Perform Marshals
|
// Perform Marshals
|
||||||
structsOut := new(bytes.Buffer)
|
structsOut := new(bytes.Buffer)
|
||||||
if err := MarshalManyPayload(structsOut, structs); err != nil {
|
if err := MarshalPayload(structsOut, structs); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
interfacesOut := new(bytes.Buffer)
|
interfacesOut := new(bytes.Buffer)
|
||||||
if err := MarshalManyPayload(interfacesOut, interfaces); err != nil {
|
if err := MarshalPayload(interfacesOut, interfaces); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,15 +804,15 @@ func TestMarshalMany_SliceOfInterfaceAndSliceOfStructsSameJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalMany_InvalidIntefaceArgument(t *testing.T) {
|
func TestMarshal_InvalidIntefaceArgument(t *testing.T) {
|
||||||
out := new(bytes.Buffer)
|
out := new(bytes.Buffer)
|
||||||
if err := MarshalManyPayload(out, true); err != ErrExpectedSlice {
|
if err := MarshalPayload(out, true); err != ErrUnexpectedType {
|
||||||
t.Fatal("Was expecting an error")
|
t.Fatal("Was expecting an error")
|
||||||
}
|
}
|
||||||
if err := MarshalManyPayload(out, 25); err != ErrExpectedSlice {
|
if err := MarshalPayload(out, 25); err != ErrUnexpectedType {
|
||||||
t.Fatal("Was expecting an error")
|
t.Fatal("Was expecting an error")
|
||||||
}
|
}
|
||||||
if err := MarshalManyPayload(out, Book{}); err != ErrExpectedSlice {
|
if err := MarshalPayload(out, Book{}); err != ErrUnexpectedType {
|
||||||
t.Fatal("Was expecting an error")
|
t.Fatal("Was expecting an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
runtime.go
16
runtime.go
|
@ -60,21 +60,9 @@ func (r *Runtime) UnmarshalManyPayload(reader io.Reader, kind reflect.Type) (ele
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) MarshalOnePayload(w io.Writer, model interface{}) error {
|
func (r *Runtime) MarshalPayload(w io.Writer, model interface{}) error {
|
||||||
return r.instrumentCall(MarshalStart, MarshalStop, func() error {
|
return r.instrumentCall(MarshalStart, MarshalStop, func() error {
|
||||||
return MarshalOnePayload(w, model)
|
return MarshalPayload(w, model)
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Runtime) MarshalManyPayload(w io.Writer, models interface{}) error {
|
|
||||||
return r.instrumentCall(MarshalStart, MarshalStop, func() error {
|
|
||||||
return MarshalManyPayload(w, models)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Runtime) MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error {
|
|
||||||
return r.instrumentCall(MarshalStart, MarshalStop, func() error {
|
|
||||||
return MarshalOnePayloadEmbedded(w, model)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue