summaryrefslogtreecommitdiff
path: root/vendor/github.com/google/jsonapi
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-05-11 21:12:57 -0600
committermo khan <mo@mokhan.ca>2025-05-11 21:12:57 -0600
commit60440f90dca28e99a31dd328c5f6d5dc0f9b6a2e (patch)
tree2f54adf55086516f162f0a55a5347e6b25f7f176 /vendor/github.com/google/jsonapi
parent05ca9b8d3a9c7203a3a3b590beaa400900bd9007 (diff)
chore: vendor go dependencies
Diffstat (limited to 'vendor/github.com/google/jsonapi')
-rw-r--r--vendor/github.com/google/jsonapi/.gitignore1
-rw-r--r--vendor/github.com/google/jsonapi/.travis.yml13
-rw-r--r--vendor/github.com/google/jsonapi/LICENSE21
-rw-r--r--vendor/github.com/google/jsonapi/README.md477
-rw-r--r--vendor/github.com/google/jsonapi/constants.go56
-rw-r--r--vendor/github.com/google/jsonapi/doc.go70
-rw-r--r--vendor/github.com/google/jsonapi/errors.go52
-rw-r--r--vendor/github.com/google/jsonapi/node.go121
-rw-r--r--vendor/github.com/google/jsonapi/request.go656
-rw-r--r--vendor/github.com/google/jsonapi/response.go538
-rw-r--r--vendor/github.com/google/jsonapi/runtime.go129
11 files changed, 2134 insertions, 0 deletions
diff --git a/vendor/github.com/google/jsonapi/.gitignore b/vendor/github.com/google/jsonapi/.gitignore
new file mode 100644
index 0000000..19b1e1c
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/.gitignore
@@ -0,0 +1 @@
+/examples/examples
diff --git a/vendor/github.com/google/jsonapi/.travis.yml b/vendor/github.com/google/jsonapi/.travis.yml
new file mode 100644
index 0000000..abc7d1b
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/.travis.yml
@@ -0,0 +1,13 @@
+language: go
+arch:
+ - amd64
+ - ppc64le
+go:
+ - 1.11.x
+ - 1.12.x
+ - 1.13.x
+ - 1.14.x
+ - 1.15.x
+ - 1.16.x
+ - tip
+script: go test ./... -v
diff --git a/vendor/github.com/google/jsonapi/LICENSE b/vendor/github.com/google/jsonapi/LICENSE
new file mode 100644
index 0000000..c97912c
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Google Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/google/jsonapi/README.md b/vendor/github.com/google/jsonapi/README.md
new file mode 100644
index 0000000..8dfb943
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/README.md
@@ -0,0 +1,477 @@
+# jsonapi
+
+[![Build Status](https://travis-ci.org/google/jsonapi.svg?branch=master)](https://travis-ci.org/google/jsonapi)
+[![Go Report Card](https://goreportcard.com/badge/github.com/google/jsonapi)](https://goreportcard.com/report/github.com/google/jsonapi)
+[![GoDoc](https://godoc.org/github.com/google/jsonapi?status.svg)](http://godoc.org/github.com/google/jsonapi)
+[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)
+
+A serializer/deserializer for JSON payloads that comply to the
+[JSON API - jsonapi.org](http://jsonapi.org) spec in go.
+
+
+
+## Installation
+
+```
+go get -u github.com/google/jsonapi
+```
+
+Or, see [Alternative Installation](#alternative-installation).
+
+## Background
+
+You are working in your Go web application and you have a struct that is
+organized similarly to your database schema. You need to send and
+receive json payloads that adhere to the JSON API spec. Once you realize that
+your json needed to take on this special form, you go down the path of
+creating more structs to be able to serialize and deserialize JSON API
+payloads. Then there are more models required with this additional
+structure. Ugh! With JSON API, you can keep your model structs as is and
+use [StructTags](http://golang.org/pkg/reflect/#StructTag) to indicate
+to JSON API how you want your response built or your request
+deserialized. What about your relationships? JSON API supports
+relationships out of the box and will even put them in your response
+into an `included` side-loaded slice--that contains associated records.
+
+## Introduction
+
+JSON API uses [StructField](http://golang.org/pkg/reflect/#StructField)
+tags to annotate the structs fields that you already have and use in
+your app and then reads and writes [JSON API](http://jsonapi.org)
+output based on the instructions you give the library in your JSON API
+tags. Let's take an example. In your app, you most likely have structs
+that look similar to these:
+
+
+```go
+type Blog struct {
+ ID int `json:"id"`
+ Title string `json:"title"`
+ Posts []*Post `json:"posts"`
+ CurrentPost *Post `json:"current_post"`
+ CurrentPostId int `json:"current_post_id"`
+ CreatedAt time.Time `json:"created_at"`
+ ViewCount int `json:"view_count"`
+}
+
+type Post struct {
+ ID int `json:"id"`
+ BlogID int `json:"blog_id"`
+ Title string `json:"title"`
+ Body string `json:"body"`
+ Comments []*Comment `json:"comments"`
+}
+
+type Comment struct {
+ Id int `json:"id"`
+ PostID int `json:"post_id"`
+ Body string `json:"body"`
+ Likes uint `json:"likes_count,omitempty"`
+}
+```
+
+These structs may or may not resemble the layout of your database. But
+these are the ones that you want to use right? You wouldn't want to use
+structs like those that JSON API sends because it is difficult to get at
+all of your data easily.
+
+## Example App
+
+[examples/app.go](https://github.com/google/jsonapi/blob/master/examples/app.go)
+
+This program demonstrates the implementation of a create, a show,
+and a list [http.Handler](http://golang.org/pkg/net/http#Handler). It
+outputs some example requests and responses as well as serialized
+examples of the source/target structs to json. That is to say, I show
+you that the library has successfully taken your JSON API request and
+turned it into your struct types.
+
+To run,
+
+* Make sure you have [Go installed](https://golang.org/doc/install)
+* Create the following directories or similar: `~/go`
+* Set `GOPATH` to `PWD` in your shell session, `export GOPATH=$PWD`
+* `go get github.com/google/jsonapi`. (Append `-u` after `get` if you
+ are updating.)
+* `cd $GOPATH/src/github.com/google/jsonapi/examples`
+* `go build && ./examples`
+
+## `jsonapi` Tag Reference
+
+### Example
+
+The `jsonapi` [StructTags](http://golang.org/pkg/reflect/#StructTag)
+tells this library how to marshal and unmarshal your structs into
+JSON API payloads and your JSON API payloads to structs, respectively.
+Then Use JSON API's Marshal and Unmarshal methods to construct and read
+your responses and replies. Here's an example of the structs above
+using JSON API tags:
+
+```go
+type Blog struct {
+ ID int `jsonapi:"primary,blogs"`
+ Title string `jsonapi:"attr,title"`
+ Posts []*Post `jsonapi:"relation,posts"`
+ CurrentPost *Post `jsonapi:"relation,current_post"`
+ CurrentPostID int `jsonapi:"attr,current_post_id"`
+ CreatedAt time.Time `jsonapi:"attr,created_at"`
+ ViewCount int `jsonapi:"attr,view_count"`
+}
+
+type Post struct {
+ ID int `jsonapi:"primary,posts"`
+ BlogID int `jsonapi:"attr,blog_id"`
+ Title string `jsonapi:"attr,title"`
+ Body string `jsonapi:"attr,body"`
+ Comments []*Comment `jsonapi:"relation,comments"`
+}
+
+type Comment struct {
+ ID int `jsonapi:"primary,comments"`
+ PostID int `jsonapi:"attr,post_id"`
+ Body string `jsonapi:"attr,body"`
+ Likes uint `jsonapi:"attr,likes-count,omitempty"`
+}
+```
+
+### Permitted Tag Values
+
+#### `primary`
+
+```
+`jsonapi:"primary,<type field output>"`
+```
+
+This indicates this is the primary key field for this struct type.
+Tag value arguments are comma separated. The first argument must be,
+`primary`, and the second must be the name that should appear in the
+`type`\* field for all data objects that represent this type of model.
+
+\* According the [JSON API](http://jsonapi.org) spec, the plural record
+types are shown in the examples, but not required.
+
+#### `attr`
+
+```
+`jsonapi:"attr,<key name in attributes hash>,<optional: omitempty>"`
+```
+
+These fields' values will end up in the `attributes`hash for a record.
+The first argument must be, `attr`, and the second should be the name
+for the key to display in the `attributes` hash for that record. The optional
+third argument is `omitempty` - if it is present the field will not be present
+in the `"attributes"` if the field's value is equivalent to the field types
+empty value (ie if the `count` field is of type `int`, `omitempty` will omit the
+field when `count` has a value of `0`). Lastly, the spec indicates that
+`attributes` key names should be dasherized for multiple word field names.
+
+#### `relation`
+
+```
+`jsonapi:"relation,<key name in relationships hash>,<optional: omitempty>"`
+```
+
+Relations are struct fields that represent a one-to-one or one-to-many
+relationship with other structs. JSON API will traverse the graph of
+relationships and marshal or unmarshal records. The first argument must
+be, `relation`, and the second should be the name of the relationship,
+used as the key in the `relationships` hash for the record. The optional
+third argument is `omitempty` - if present will prevent non existent to-one and
+to-many from being serialized.
+
+## Methods Reference
+
+**All `Marshal` and `Unmarshal` methods expect pointers to struct
+instance or slices of the same contained with the `interface{}`s**
+
+Now you have your structs prepared to be serialized or materialized, What
+about the rest?
+
+### Create Record Example
+
+You can Unmarshal a JSON API payload using
+[jsonapi.UnmarshalPayload](http://godoc.org/github.com/google/jsonapi#UnmarshalPayload).
+It reads from an [io.Reader](https://golang.org/pkg/io/#Reader)
+containing a JSON API payload for one record (but can have related
+records). Then, it materializes a struct that you created and passed in
+(using new or &). Again, the method supports single records only, at
+the top level, in request payloads at the moment. Bulk creates and
+updates are not supported yet.
+
+After saving your record, you can use,
+[MarshalOnePayload](http://godoc.org/github.com/google/jsonapi#MarshalOnePayload),
+to write the JSON API response to an
+[io.Writer](https://golang.org/pkg/io/#Writer).
+
+#### `UnmarshalPayload`
+
+```go
+UnmarshalPayload(in io.Reader, model interface{})
+```
+
+Visit [godoc](http://godoc.org/github.com/google/jsonapi#UnmarshalPayload)
+
+#### `MarshalPayload`
+
+```go
+MarshalPayload(w io.Writer, models interface{}) error
+```
+
+Visit [godoc](http://godoc.org/github.com/google/jsonapi#MarshalPayload)
+
+Writes a JSON API response, with related records sideloaded, into an
+`included` array. This method encodes a response for either a single record or
+many records.
+
+##### Handler Example Code
+
+```go
+func CreateBlog(w http.ResponseWriter, r *http.Request) {
+ blog := new(Blog)
+
+ if err := jsonapi.UnmarshalPayload(r.Body, blog); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // ...save your blog...
+
+ w.Header().Set("Content-Type", jsonapi.MediaType)
+ w.WriteHeader(http.StatusCreated)
+
+ if err := jsonapi.MarshalPayload(w, blog); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+```
+
+### Create Records Example
+
+#### `UnmarshalManyPayload`
+
+```go
+UnmarshalManyPayload(in io.Reader, t reflect.Type) ([]interface{}, error)
+```
+
+Visit [godoc](http://godoc.org/github.com/google/jsonapi#UnmarshalManyPayload)
+
+Takes an `io.Reader` and a `reflect.Type` representing the uniform type
+contained within the `"data"` JSON API member.
+
+##### Handler Example Code
+
+```go
+func CreateBlogs(w http.ResponseWriter, r *http.Request) {
+ // ...create many blogs at once
+
+ blogs, err := UnmarshalManyPayload(r.Body, reflect.TypeOf(new(Blog)))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, blog := range blogs {
+ b, ok := blog.(*Blog)
+ // ...save each of your blogs
+ }
+
+ w.Header().Set("Content-Type", jsonapi.MediaType)
+ w.WriteHeader(http.StatusCreated)
+
+ if err := jsonapi.MarshalPayload(w, blogs); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+```
+
+
+### Links
+
+If you need to include [link objects](http://jsonapi.org/format/#document-links) along with response data, implement the `Linkable` interface for document-links, and `RelationshipLinkable` for relationship links:
+
+```go
+func (post Post) JSONAPILinks() *Links {
+ return &Links{
+ "self": "href": fmt.Sprintf("https://example.com/posts/%d", post.ID),
+ "comments": Link{
+ Href: fmt.Sprintf("https://example.com/api/blogs/%d/comments", post.ID),
+ Meta: map[string]interface{}{
+ "counts": map[string]uint{
+ "likes": 4,
+ },
+ },
+ },
+ }
+}
+
+// Invoked for each relationship defined on the Post struct when marshaled
+func (post Post) JSONAPIRelationshipLinks(relation string) *Links {
+ if relation == "comments" {
+ return &Links{
+ "related": fmt.Sprintf("https://example.com/posts/%d/comments", post.ID),
+ }
+ }
+ return nil
+}
+```
+
+### Meta
+
+ If you need to include [meta objects](http://jsonapi.org/format/#document-meta) along with response data, implement the `Metable` interface for document-meta, and `RelationshipMetable` for relationship meta:
+
+ ```go
+func (post Post) JSONAPIMeta() *Meta {
+ return &Meta{
+ "details": "sample details here",
+ }
+}
+
+// Invoked for each relationship defined on the Post struct when marshaled
+func (post Post) JSONAPIRelationshipMeta(relation string) *Meta {
+ if relation == "comments" {
+ return &Meta{
+ "this": map[string]interface{}{
+ "can": map[string]interface{}{
+ "go": []interface{}{
+ "as",
+ "deep",
+ map[string]interface{}{
+ "as": "required",
+ },
+ },
+ },
+ },
+ }
+ }
+ return nil
+}
+```
+
+### Custom types
+
+Custom types are supported for primitive types, only, as attributes. Examples,
+
+```go
+type CustomIntType int
+type CustomFloatType float64
+type CustomStringType string
+```
+
+Types like following are not supported, but may be in the future:
+
+```go
+type CustomMapType map[string]interface{}
+type CustomSliceMapType []map[string]interface{}
+```
+
+### Errors
+This package also implements support for JSON API compatible `errors` payloads using the following types.
+
+#### `MarshalErrors`
+```go
+MarshalErrors(w io.Writer, errs []*ErrorObject) error
+```
+
+Writes a JSON API response using the given `[]error`.
+
+#### `ErrorsPayload`
+```go
+type ErrorsPayload struct {
+ Errors []*ErrorObject `json:"errors"`
+}
+```
+
+ErrorsPayload is a serializer struct for representing a valid JSON API errors payload.
+
+#### `ErrorObject`
+```go
+type ErrorObject struct { ... }
+
+// Error implements the `Error` interface.
+func (e *ErrorObject) Error() string {
+ return fmt.Sprintf("Error: %s %s\n", e.Title, e.Detail)
+}
+```
+
+ErrorObject is an `Error` implementation as well as an implementation of the JSON API error object.
+
+The main idea behind this struct is that you can use it directly in your code as an error type and pass it directly to `MarshalErrors` to get a valid JSON API errors payload.
+
+##### Errors Example Code
+```go
+// An error has come up in your code, so set an appropriate status, and serialize the error.
+if err := validate(&myStructToValidate); err != nil {
+ context.SetStatusCode(http.StatusBadRequest) // Or however you need to set a status.
+ jsonapi.MarshalErrors(w, []*ErrorObject{{
+ Title: "Validation Error",
+ Detail: "Given request body was invalid.",
+ Status: "400",
+ Meta: map[string]interface{}{"field": "some_field", "error": "bad type", "expected": "string", "received": "float64"},
+ }})
+ return
+}
+```
+
+## Testing
+
+### `MarshalOnePayloadEmbedded`
+
+```go
+MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error
+```
+
+Visit [godoc](http://godoc.org/github.com/google/jsonapi#MarshalOnePayloadEmbedded)
+
+This method is not strictly meant to for use in implementation code,
+although feel free. It was mainly created for use in tests; in most cases,
+your 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 response. In other words, there will be no,
+`included`, array in the json; all relationships will be serialized
+inline with the data.
+
+However, in tests, you may want to construct payloads to post to create
+methods that are embedded to most closely model the payloads that will
+be produced by the client. This method aims to enable that.
+
+### Example
+
+```go
+out := bytes.NewBuffer(nil)
+
+// testModel returns a pointer to a Blog
+jsonapi.MarshalOnePayloadEmbedded(out, testModel())
+
+h := new(BlogsHandler)
+
+w := httptest.NewRecorder()
+r, _ := http.NewRequest(http.MethodPost, "/blogs", out)
+
+h.CreateBlog(w, r)
+
+blog := new(Blog)
+jsonapi.UnmarshalPayload(w.Body, blog)
+
+// ... assert stuff about blog here ...
+```
+
+## Alternative Installation
+I use git subtrees to manage dependencies rather than `go get` so that
+the src is committed to my repo.
+
+```
+git subtree add --squash --prefix=src/github.com/google/jsonapi https://github.com/google/jsonapi.git master
+```
+
+To update,
+
+```
+git subtree pull --squash --prefix=src/github.com/google/jsonapi https://github.com/google/jsonapi.git master
+```
+
+This assumes that I have my repo structured with a `src` dir containing
+a collection of packages and `GOPATH` is set to the root
+folder--containing `src`.
+
+## Contributing
+
+Fork, Change, Pull Request *with tests*.
diff --git a/vendor/github.com/google/jsonapi/constants.go b/vendor/github.com/google/jsonapi/constants.go
new file mode 100644
index 0000000..35bbe05
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/constants.go
@@ -0,0 +1,56 @@
+package jsonapi
+
+const (
+ // StructTag annotation strings
+ annotationJSONAPI = "jsonapi"
+ annotationPrimary = "primary"
+ annotationClientID = "client-id"
+ annotationAttribute = "attr"
+ annotationRelation = "relation"
+ annotationOmitEmpty = "omitempty"
+ annotationISO8601 = "iso8601"
+ annotationRFC3339 = "rfc3339"
+ annotationSeperator = ","
+
+ iso8601TimeFormat = "2006-01-02T15:04:05Z"
+
+ // MediaType is the identifier for the JSON API media type
+ //
+ // see http://jsonapi.org/format/#document-structure
+ MediaType = "application/vnd.api+json"
+
+ // Pagination Constants
+ //
+ // http://jsonapi.org/format/#fetching-pagination
+
+ // KeyFirstPage is the key to the links object whose value contains a link to
+ // the first page of data
+ KeyFirstPage = "first"
+ // KeyLastPage is the key to the links object whose value contains a link to
+ // the last page of data
+ KeyLastPage = "last"
+ // KeyPreviousPage is the key to the links object whose value contains a link
+ // to the previous page of data
+ KeyPreviousPage = "prev"
+ // KeyNextPage is the key to the links object whose value contains a link to
+ // the next page of data
+ KeyNextPage = "next"
+
+ // QueryParamPageNumber is a JSON API query parameter used in a page based
+ // pagination strategy in conjunction with QueryParamPageSize
+ QueryParamPageNumber = "page[number]"
+ // QueryParamPageSize is a JSON API query parameter used in a page based
+ // pagination strategy in conjunction with QueryParamPageNumber
+ QueryParamPageSize = "page[size]"
+
+ // QueryParamPageOffset is a JSON API query parameter used in an offset based
+ // pagination strategy in conjunction with QueryParamPageLimit
+ QueryParamPageOffset = "page[offset]"
+ // QueryParamPageLimit is a JSON API query parameter used in an offset based
+ // pagination strategy in conjunction with QueryParamPageOffset
+ QueryParamPageLimit = "page[limit]"
+
+ // QueryParamPageCursor is a JSON API query parameter used with a cursor-based
+ // strategy
+ QueryParamPageCursor = "page[cursor]"
+)
diff --git a/vendor/github.com/google/jsonapi/doc.go b/vendor/github.com/google/jsonapi/doc.go
new file mode 100644
index 0000000..ba4068a
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/doc.go
@@ -0,0 +1,70 @@
+/*
+Package jsonapi provides a serializer and deserializer for jsonapi.org spec payloads.
+
+You can keep your model structs as is and use struct field tags to indicate to jsonapi
+how you want your response built or your request deserialized. What about my relationships?
+jsonapi supports relationships out of the box and will even side load them in your response
+into an "included" array--that contains associated objects.
+
+jsonapi uses StructField tags to annotate the structs fields that you already have and use
+in your app and then reads and writes jsonapi.org output based on the instructions you give
+the library in your jsonapi tags.
+
+Example structs using a Blog > Post > Comment structure,
+
+ type Blog struct {
+ ID int `jsonapi:"primary,blogs"`
+ Title string `jsonapi:"attr,title"`
+ Posts []*Post `jsonapi:"relation,posts"`
+ CurrentPost *Post `jsonapi:"relation,current_post"`
+ CurrentPostID int `jsonapi:"attr,current_post_id"`
+ CreatedAt time.Time `jsonapi:"attr,created_at"`
+ ViewCount int `jsonapi:"attr,view_count"`
+ }
+
+ type Post struct {
+ ID int `jsonapi:"primary,posts"`
+ BlogID int `jsonapi:"attr,blog_id"`
+ Title string `jsonapi:"attr,title"`
+ Body string `jsonapi:"attr,body"`
+ Comments []*Comment `jsonapi:"relation,comments"`
+ }
+
+ type Comment struct {
+ ID int `jsonapi:"primary,comments"`
+ PostID int `jsonapi:"attr,post_id"`
+ Body string `jsonapi:"attr,body"`
+ }
+
+jsonapi Tag Reference
+
+Value, primary: "primary,<type field output>"
+
+This indicates that this is the primary key field for this struct type. Tag
+value arguments are comma separated. The first argument must be, "primary", and
+the second must be the name that should appear in the "type" field for all data
+objects that represent this type of model.
+
+Value, attr: "attr,<key name in attributes hash>[,<extra arguments>]"
+
+These fields' values should end up in the "attribute" hash for a record. The first
+argument must be, "attr', and the second should be the name for the key to display in
+the "attributes" hash for that record.
+
+The following extra arguments are also supported:
+
+"omitempty": excludes the fields value from the "attribute" hash.
+"iso8601": uses the ISO8601 timestamp format when serialising or deserialising the time.Time value.
+
+Value, relation: "relation,<key name in relationships hash>"
+
+Relations are struct fields that represent a one-to-one or one-to-many to other structs.
+jsonapi will traverse the graph of relationships and marshal or unmarshal records. The first
+argument must be, "relation", and the second should be the name of the relationship, used as
+the key in the "relationships" hash for the record.
+
+Use the methods below to Marshal and Unmarshal jsonapi.org json payloads.
+
+Visit the readme at https://github.com/google/jsonapi
+*/
+package jsonapi
diff --git a/vendor/github.com/google/jsonapi/errors.go b/vendor/github.com/google/jsonapi/errors.go
new file mode 100644
index 0000000..798fed0
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/errors.go
@@ -0,0 +1,52 @@
+package jsonapi
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+)
+
+// MarshalErrors writes a JSON API response using the given `[]error`.
+//
+// For more information on JSON API error payloads, see the spec here:
+// http://jsonapi.org/format/#document-top-level
+// and here: http://jsonapi.org/format/#error-objects.
+func MarshalErrors(w io.Writer, errorObjects []*ErrorObject) error {
+ return json.NewEncoder(w).Encode(&ErrorsPayload{Errors: errorObjects})
+}
+
+// ErrorsPayload is a serializer struct for representing a valid JSON API errors payload.
+type ErrorsPayload struct {
+ Errors []*ErrorObject `json:"errors"`
+}
+
+// ErrorObject is an `Error` implementation as well as an implementation of the JSON API error object.
+//
+// The main idea behind this struct is that you can use it directly in your code as an error type
+// and pass it directly to `MarshalErrors` to get a valid JSON API errors payload.
+// For more information on Golang errors, see: https://golang.org/pkg/errors/
+// For more information on the JSON API spec's error objects, see: http://jsonapi.org/format/#error-objects
+type ErrorObject struct {
+ // ID is a unique identifier for this particular occurrence of a problem.
+ ID string `json:"id,omitempty"`
+
+ // Title is a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
+ Title string `json:"title,omitempty"`
+
+ // Detail is a human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.
+ Detail string `json:"detail,omitempty"`
+
+ // Status is the HTTP status code applicable to this problem, expressed as a string value.
+ Status string `json:"status,omitempty"`
+
+ // Code is an application-specific error code, expressed as a string value.
+ Code string `json:"code,omitempty"`
+
+ // Meta is an object containing non-standard meta-information about the error.
+ Meta *map[string]interface{} `json:"meta,omitempty"`
+}
+
+// Error implements the `Error` interface.
+func (e *ErrorObject) Error() string {
+ return fmt.Sprintf("Error: %s %s\n", e.Title, e.Detail)
+}
diff --git a/vendor/github.com/google/jsonapi/node.go b/vendor/github.com/google/jsonapi/node.go
new file mode 100644
index 0000000..a58488c
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/node.go
@@ -0,0 +1,121 @@
+package jsonapi
+
+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
+// resource (Node) was included as an {} in the "data" key
+type OnePayload struct {
+ Data *Node `json:"data"`
+ Included []*Node `json:"included,omitempty"`
+ Links *Links `json:"links,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
+// resources (Nodes) were included in an [] in the "data" key
+type ManyPayload struct {
+ Data []*Node `json:"data"`
+ Included []*Node `json:"included,omitempty"`
+ Links *Links `json:"links,omitempty"`
+ Meta *Meta `json:"meta,omitempty"`
+}
+
+func (p *ManyPayload) clearIncluded() {
+ p.Included = []*Node{}
+}
+
+// Node is used to represent a generic JSON API Resource
+type Node struct {
+ Type string `json:"type"`
+ ID string `json:"id,omitempty"`
+ ClientID string `json:"client-id,omitempty"`
+ Attributes map[string]interface{} `json:"attributes,omitempty"`
+ Relationships map[string]interface{} `json:"relationships,omitempty"`
+ Links *Links `json:"links,omitempty"`
+ Meta *Meta `json:"meta,omitempty"`
+}
+
+// RelationshipOneNode is used to represent a generic has one JSON API relation
+type RelationshipOneNode struct {
+ Data *Node `json:"data"`
+ Links *Links `json:"links,omitempty"`
+ Meta *Meta `json:"meta,omitempty"`
+}
+
+// RelationshipManyNode is used to represent a generic has many JSON API
+// relation
+type RelationshipManyNode struct {
+ Data []*Node `json:"data"`
+ Links *Links `json:"links,omitempty"`
+ Meta *Meta `json:"meta,omitempty"`
+}
+
+// Links is used to represent a `links` object.
+// http://jsonapi.org/format/#document-links
+type Links map[string]interface{}
+
+func (l *Links) validate() (err error) {
+ // Each member of a links object is a “link”. A link MUST be represented as
+ // either:
+ // - a string containing the link’s URL.
+ // - an object (“link object”) which can contain the following members:
+ // - href: a string containing the link’s URL.
+ // - meta: a meta object containing non-standard meta-information about the
+ // link.
+ for k, v := range *l {
+ _, isString := v.(string)
+ _, isLink := v.(Link)
+
+ if !(isString || isLink) {
+ return fmt.Errorf(
+ "The %s member of the links object was not a string or link object",
+ k,
+ )
+ }
+ }
+ return
+}
+
+// Link is used to represent a member of the `links` object.
+type Link struct {
+ Href string `json:"href"`
+ Meta Meta `json:"meta,omitempty"`
+}
+
+// Linkable is used to include document links in response data
+// e.g. {"self": "http://example.com/posts/1"}
+type Linkable interface {
+ JSONAPILinks() *Links
+}
+
+// RelationshipLinkable is used to include relationship links in response data
+// e.g. {"related": "http://example.com/posts/1/comments"}
+type RelationshipLinkable interface {
+ // JSONAPIRelationshipLinks will be invoked for each relationship with the corresponding relation name (e.g. `comments`)
+ JSONAPIRelationshipLinks(relation string) *Links
+}
+
+// Meta is used to represent a `meta` object.
+// http://jsonapi.org/format/#document-meta
+type Meta map[string]interface{}
+
+// Metable is used to include document meta in response data
+// e.g. {"foo": "bar"}
+type Metable interface {
+ JSONAPIMeta() *Meta
+}
+
+// RelationshipMetable is used to include relationship meta in response data
+type RelationshipMetable interface {
+ // JSONRelationshipMeta will be invoked for each relationship with the corresponding relation name (e.g. `comments`)
+ JSONAPIRelationshipMeta(relation string) *Meta
+}
diff --git a/vendor/github.com/google/jsonapi/request.go b/vendor/github.com/google/jsonapi/request.go
new file mode 100644
index 0000000..f665857
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/request.go
@@ -0,0 +1,656 @@
+package jsonapi
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ unsupportedStructTagMsg = "Unsupported jsonapi tag annotation, %s"
+)
+
+var (
+ // ErrInvalidTime is returned when a struct has a time.Time type field, but
+ // the JSON value was not a unix timestamp integer.
+ ErrInvalidTime = errors.New("Only numbers can be parsed as dates, unix timestamps")
+ // ErrInvalidISO8601 is returned when a struct has a time.Time type field and includes
+ // "iso8601" in the tag spec, but the JSON value was not an ISO8601 timestamp string.
+ ErrInvalidISO8601 = errors.New("Only strings can be parsed as dates, ISO8601 timestamps")
+ // ErrInvalidRFC3339 is returned when a struct has a time.Time type field and includes
+ // "rfc3339" in the tag spec, but the JSON value was not an RFC3339 timestamp string.
+ ErrInvalidRFC3339 = errors.New("Only strings can be parsed as dates, RFC3339 timestamps")
+ // ErrUnknownFieldNumberType is returned when the JSON value was a float
+ // (numeric) but the Struct field was a non numeric type (i.e. not int, uint,
+ // float, etc)
+ ErrUnknownFieldNumberType = errors.New("The struct field was not of a known number type")
+ // ErrInvalidType is returned when the given type is incompatible with the expected type.
+ ErrInvalidType = errors.New("Invalid type provided") // I wish we used punctuation.
+
+)
+
+// ErrUnsupportedPtrType is returned when the Struct field was a pointer but
+// the JSON value was of a different type
+type ErrUnsupportedPtrType struct {
+ rf reflect.Value
+ t reflect.Type
+ structField reflect.StructField
+}
+
+func (eupt ErrUnsupportedPtrType) Error() string {
+ typeName := eupt.t.Elem().Name()
+ kind := eupt.t.Elem().Kind()
+ if kind.String() != "" && kind.String() != typeName {
+ typeName = fmt.Sprintf("%s (%s)", typeName, kind.String())
+ }
+ return fmt.Sprintf(
+ "jsonapi: Can't unmarshal %+v (%s) to struct field `%s`, which is a pointer to `%s`",
+ eupt.rf, eupt.rf.Type().Kind(), eupt.structField.Name, typeName,
+ )
+}
+
+func newErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField reflect.StructField) error {
+ return ErrUnsupportedPtrType{rf, t, structField}
+}
+
+// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
+// struct fields. This method supports single request payloads only, at the
+// moment. Bulk creates and updates are not supported yet.
+//
+// Will Unmarshal embedded and sideloaded payloads. The latter is only possible if the
+// object graph is complete. That is, in the "relationships" data there are type and id,
+// keys that correspond to records in the "included" array.
+//
+// For example you could pass it, in, req.Body and, model, a BlogPost
+// struct instance to populate in an http handler,
+//
+// func CreateBlog(w http.ResponseWriter, r *http.Request) {
+// blog := new(Blog)
+//
+// if err := jsonapi.UnmarshalPayload(r.Body, blog); err != nil {
+// http.Error(w, err.Error(), 500)
+// return
+// }
+//
+// // ...do stuff with your blog...
+//
+// w.Header().Set("Content-Type", jsonapi.MediaType)
+// w.WriteHeader(201)
+//
+// if err := jsonapi.MarshalPayload(w, blog); err != nil {
+// http.Error(w, err.Error(), 500)
+// }
+// }
+//
+//
+// Visit https://github.com/google/jsonapi#create for more info.
+//
+// model interface{} should be a pointer to a struct.
+func UnmarshalPayload(in io.Reader, model interface{}) error {
+ payload := new(OnePayload)
+
+ if err := json.NewDecoder(in).Decode(payload); err != nil {
+ return err
+ }
+
+ if payload.Included != nil {
+ includedMap := make(map[string]*Node)
+ for _, included := range payload.Included {
+ key := fmt.Sprintf("%s,%s", included.Type, included.ID)
+ includedMap[key] = included
+ }
+
+ return unmarshalNode(payload.Data, reflect.ValueOf(model), &includedMap)
+ }
+ return unmarshalNode(payload.Data, reflect.ValueOf(model), nil)
+}
+
+// UnmarshalManyPayload converts an io into a set of struct instances using
+// jsonapi tags on the type's struct fields.
+func UnmarshalManyPayload(in io.Reader, t reflect.Type) ([]interface{}, error) {
+ payload := new(ManyPayload)
+
+ if err := json.NewDecoder(in).Decode(payload); err != nil {
+ return nil, err
+ }
+
+ models := []interface{}{} // will be populated from the "data"
+ includedMap := map[string]*Node{} // will be populate from the "included"
+
+ if payload.Included != nil {
+ for _, included := range payload.Included {
+ key := fmt.Sprintf("%s,%s", included.Type, included.ID)
+ includedMap[key] = included
+ }
+ }
+
+ for _, data := range payload.Data {
+ model := reflect.New(t.Elem())
+ err := unmarshalNode(data, model, &includedMap)
+ if err != nil {
+ return nil, err
+ }
+ models = append(models, model.Interface())
+ }
+
+ return models, nil
+}
+
+func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("data is not a jsonapi representation of '%v'", model.Type())
+ }
+ }()
+
+ modelValue := model.Elem()
+ modelType := modelValue.Type()
+
+ var er error
+
+ for i := 0; i < modelValue.NumField(); i++ {
+ fieldType := modelType.Field(i)
+ tag := fieldType.Tag.Get("jsonapi")
+ if tag == "" {
+ continue
+ }
+
+ fieldValue := modelValue.Field(i)
+
+ args := strings.Split(tag, ",")
+ if len(args) < 1 {
+ er = ErrBadJSONAPIStructTag
+ break
+ }
+
+ annotation := args[0]
+
+ if (annotation == annotationClientID && len(args) != 1) ||
+ (annotation != annotationClientID && len(args) < 2) {
+ er = ErrBadJSONAPIStructTag
+ break
+ }
+
+ if annotation == annotationPrimary {
+ // Check the JSON API Type
+ if data.Type != args[1] {
+ er = fmt.Errorf(
+ "Trying to Unmarshal an object of type %#v, but %#v does not match",
+ data.Type,
+ args[1],
+ )
+ break
+ }
+
+ if data.ID == "" {
+ continue
+ }
+
+ // ID will have to be transmitted as astring per the JSON API spec
+ v := reflect.ValueOf(data.ID)
+
+ // Deal with PTRS
+ var kind reflect.Kind
+ if fieldValue.Kind() == reflect.Ptr {
+ kind = fieldType.Type.Elem().Kind()
+ } else {
+ kind = fieldType.Type.Kind()
+ }
+
+ // Handle String case
+ if kind == reflect.String {
+ assign(fieldValue, v)
+ continue
+ }
+
+ // Value was not a string... only other supported type was a numeric,
+ // which would have been sent as a float value.
+ floatValue, err := strconv.ParseFloat(data.ID, 64)
+ if err != nil {
+ // Could not convert the value in the "id" attr to a float
+ er = ErrBadJSONAPIID
+ break
+ }
+
+ // Convert the numeric float to one of the supported ID numeric types
+ // (int[8,16,32,64] or uint[8,16,32,64])
+ idValue, err := handleNumeric(floatValue, fieldType.Type, fieldValue)
+ if err != nil {
+ // We had a JSON float (numeric), but our field was not one of the
+ // allowed numeric types
+ er = ErrBadJSONAPIID
+ break
+ }
+
+ assign(fieldValue, idValue)
+ } else if annotation == annotationClientID {
+ if data.ClientID == "" {
+ continue
+ }
+
+ fieldValue.Set(reflect.ValueOf(data.ClientID))
+ } else if annotation == annotationAttribute {
+ attributes := data.Attributes
+
+ if attributes == nil || len(data.Attributes) == 0 {
+ continue
+ }
+
+ attribute := attributes[args[1]]
+
+ // continue if the attribute was not included in the request
+ if attribute == nil {
+ continue
+ }
+
+ structField := fieldType
+ value, err := unmarshalAttribute(attribute, args, structField, fieldValue)
+ if err != nil {
+ er = err
+ break
+ }
+
+ assign(fieldValue, value)
+ } else if annotation == annotationRelation {
+ isSlice := fieldValue.Type().Kind() == reflect.Slice
+
+ if data.Relationships == nil || data.Relationships[args[1]] == nil {
+ continue
+ }
+
+ if isSlice {
+ // to-many relationship
+ relationship := new(RelationshipManyNode)
+
+ buf := bytes.NewBuffer(nil)
+
+ json.NewEncoder(buf).Encode(data.Relationships[args[1]])
+ json.NewDecoder(buf).Decode(relationship)
+
+ data := relationship.Data
+ models := reflect.New(fieldValue.Type()).Elem()
+
+ for _, n := range data {
+ m := reflect.New(fieldValue.Type().Elem().Elem())
+
+ if err := unmarshalNode(
+ fullNode(n, included),
+ m,
+ included,
+ ); err != nil {
+ er = err
+ break
+ }
+
+ models = reflect.Append(models, m)
+ }
+
+ fieldValue.Set(models)
+ } else {
+ // to-one relationships
+ relationship := new(RelationshipOneNode)
+
+ buf := bytes.NewBuffer(nil)
+
+ json.NewEncoder(buf).Encode(
+ data.Relationships[args[1]],
+ )
+ json.NewDecoder(buf).Decode(relationship)
+
+ /*
+ http://jsonapi.org/format/#document-resource-object-relationships
+ http://jsonapi.org/format/#document-resource-object-linkage
+ relationship can have a data node set to null (e.g. to disassociate the relationship)
+ so unmarshal and set fieldValue only if data obj is not null
+ */
+ if relationship.Data == nil {
+ continue
+ }
+
+ m := reflect.New(fieldValue.Type().Elem())
+ if err := unmarshalNode(
+ fullNode(relationship.Data, included),
+ m,
+ included,
+ ); err != nil {
+ er = err
+ break
+ }
+
+ fieldValue.Set(m)
+
+ }
+
+ } else {
+ er = fmt.Errorf(unsupportedStructTagMsg, annotation)
+ }
+ }
+
+ return er
+}
+
+func fullNode(n *Node, included *map[string]*Node) *Node {
+ includedKey := fmt.Sprintf("%s,%s", n.Type, n.ID)
+
+ if included != nil && (*included)[includedKey] != nil {
+ return (*included)[includedKey]
+ }
+
+ return n
+}
+
+// assign will take the value specified and assign it to the field; if
+// field is expecting a ptr assign will assign a ptr.
+func assign(field, value reflect.Value) {
+ value = reflect.Indirect(value)
+
+ if field.Kind() == reflect.Ptr {
+ // initialize pointer so it's value
+ // can be set by assignValue
+ field.Set(reflect.New(field.Type().Elem()))
+ field = field.Elem()
+
+ }
+
+ assignValue(field, value)
+}
+
+// assign assigns the specified value to the field,
+// expecting both values not to be pointer types.
+func assignValue(field, value reflect.Value) {
+ switch field.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16,
+ reflect.Int32, reflect.Int64:
+ field.SetInt(value.Int())
+ case reflect.Uint, reflect.Uint8, reflect.Uint16,
+ reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ field.SetUint(value.Uint())
+ case reflect.Float32, reflect.Float64:
+ field.SetFloat(value.Float())
+ case reflect.String:
+ field.SetString(value.String())
+ case reflect.Bool:
+ field.SetBool(value.Bool())
+ default:
+ field.Set(value)
+ }
+}
+
+func unmarshalAttribute(
+ attribute interface{},
+ args []string,
+ structField reflect.StructField,
+ fieldValue reflect.Value) (value reflect.Value, err error) {
+ value = reflect.ValueOf(attribute)
+ fieldType := structField.Type
+
+ // Handle field of type []string
+ if fieldValue.Type() == reflect.TypeOf([]string{}) {
+ value, err = handleStringSlice(attribute)
+ return
+ }
+
+ // Handle field of type time.Time
+ if fieldValue.Type() == reflect.TypeOf(time.Time{}) ||
+ fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
+ value, err = handleTime(attribute, args, fieldValue)
+ return
+ }
+
+ // Handle field of type struct
+ if fieldValue.Type().Kind() == reflect.Struct {
+ value, err = handleStruct(attribute, fieldValue)
+ return
+ }
+
+ // Handle field containing slice of structs
+ if fieldValue.Type().Kind() == reflect.Slice &&
+ reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
+ value, err = handleStructSlice(attribute, fieldValue)
+ return
+ }
+
+ // JSON value was a float (numeric)
+ if value.Kind() == reflect.Float64 {
+ value, err = handleNumeric(attribute, fieldType, fieldValue)
+ return
+ }
+
+ // Field was a Pointer type
+ if fieldValue.Kind() == reflect.Ptr {
+ value, err = handlePointer(attribute, args, fieldType, fieldValue, structField)
+ return
+ }
+
+ // As a final catch-all, ensure types line up to avoid a runtime panic.
+ if fieldValue.Kind() != value.Kind() {
+ err = ErrInvalidType
+ return
+ }
+
+ return
+}
+
+func handleStringSlice(attribute interface{}) (reflect.Value, error) {
+ v := reflect.ValueOf(attribute)
+ values := make([]string, v.Len())
+ for i := 0; i < v.Len(); i++ {
+ values[i] = v.Index(i).Interface().(string)
+ }
+
+ return reflect.ValueOf(values), nil
+}
+
+func handleTime(attribute interface{}, args []string, fieldValue reflect.Value) (reflect.Value, error) {
+ var isISO8601, isRFC3339 bool
+ v := reflect.ValueOf(attribute)
+
+ if len(args) > 2 {
+ for _, arg := range args[2:] {
+ if arg == annotationISO8601 {
+ isISO8601 = true
+ } else if arg == annotationRFC3339 {
+ isRFC3339 = true
+ }
+ }
+ }
+
+ if isISO8601 {
+ if v.Kind() != reflect.String {
+ return reflect.ValueOf(time.Now()), ErrInvalidISO8601
+ }
+
+ t, err := time.Parse(iso8601TimeFormat, v.Interface().(string))
+ if err != nil {
+ return reflect.ValueOf(time.Now()), ErrInvalidISO8601
+ }
+
+ if fieldValue.Kind() == reflect.Ptr {
+ return reflect.ValueOf(&t), nil
+ }
+
+ return reflect.ValueOf(t), nil
+ }
+
+ if isRFC3339 {
+ if v.Kind() != reflect.String {
+ return reflect.ValueOf(time.Now()), ErrInvalidRFC3339
+ }
+
+ t, err := time.Parse(time.RFC3339, v.Interface().(string))
+ if err != nil {
+ return reflect.ValueOf(time.Now()), ErrInvalidRFC3339
+ }
+
+ if fieldValue.Kind() == reflect.Ptr {
+ return reflect.ValueOf(&t), nil
+ }
+
+ return reflect.ValueOf(t), nil
+ }
+
+ var at int64
+
+ if v.Kind() == reflect.Float64 {
+ at = int64(v.Interface().(float64))
+ } else if v.Kind() == reflect.Int {
+ at = v.Int()
+ } else {
+ return reflect.ValueOf(time.Now()), ErrInvalidTime
+ }
+
+ t := time.Unix(at, 0)
+
+ return reflect.ValueOf(t), nil
+}
+
+func handleNumeric(
+ attribute interface{},
+ fieldType reflect.Type,
+ fieldValue reflect.Value) (reflect.Value, error) {
+ v := reflect.ValueOf(attribute)
+ floatValue := v.Interface().(float64)
+
+ var kind reflect.Kind
+ if fieldValue.Kind() == reflect.Ptr {
+ kind = fieldType.Elem().Kind()
+ } else {
+ kind = fieldType.Kind()
+ }
+
+ var numericValue reflect.Value
+
+ switch kind {
+ case reflect.Int:
+ n := int(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Int8:
+ n := int8(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Int16:
+ n := int16(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Int32:
+ n := int32(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Int64:
+ n := int64(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Uint:
+ n := uint(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Uint8:
+ n := uint8(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Uint16:
+ n := uint16(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Uint32:
+ n := uint32(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Uint64:
+ n := uint64(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Float32:
+ n := float32(floatValue)
+ numericValue = reflect.ValueOf(&n)
+ case reflect.Float64:
+ n := floatValue
+ numericValue = reflect.ValueOf(&n)
+ default:
+ return reflect.Value{}, ErrUnknownFieldNumberType
+ }
+
+ return numericValue, nil
+}
+
+func handlePointer(
+ attribute interface{},
+ args []string,
+ fieldType reflect.Type,
+ fieldValue reflect.Value,
+ structField reflect.StructField) (reflect.Value, error) {
+ t := fieldValue.Type()
+ var concreteVal reflect.Value
+
+ switch cVal := attribute.(type) {
+ case string:
+ concreteVal = reflect.ValueOf(&cVal)
+ case bool:
+ concreteVal = reflect.ValueOf(&cVal)
+ case complex64, complex128, uintptr:
+ concreteVal = reflect.ValueOf(&cVal)
+ case map[string]interface{}:
+ var err error
+ concreteVal, err = handleStruct(attribute, fieldValue)
+ if err != nil {
+ return reflect.Value{}, newErrUnsupportedPtrType(
+ reflect.ValueOf(attribute), fieldType, structField)
+ }
+ return concreteVal, err
+ default:
+ return reflect.Value{}, newErrUnsupportedPtrType(
+ reflect.ValueOf(attribute), fieldType, structField)
+ }
+
+ if t != concreteVal.Type() {
+ return reflect.Value{}, newErrUnsupportedPtrType(
+ reflect.ValueOf(attribute), fieldType, structField)
+ }
+
+ return concreteVal, nil
+}
+
+func handleStruct(
+ attribute interface{},
+ fieldValue reflect.Value) (reflect.Value, error) {
+
+ data, err := json.Marshal(attribute)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+
+ node := new(Node)
+ if err := json.Unmarshal(data, &node.Attributes); err != nil {
+ return reflect.Value{}, err
+ }
+
+ var model reflect.Value
+ if fieldValue.Kind() == reflect.Ptr {
+ model = reflect.New(fieldValue.Type().Elem())
+ } else {
+ model = reflect.New(fieldValue.Type())
+ }
+
+ if err := unmarshalNode(node, model, nil); err != nil {
+ return reflect.Value{}, err
+ }
+
+ return model, nil
+}
+
+func handleStructSlice(
+ attribute interface{},
+ fieldValue reflect.Value) (reflect.Value, error) {
+ models := reflect.New(fieldValue.Type()).Elem()
+ dataMap := reflect.ValueOf(attribute).Interface().([]interface{})
+ for _, data := range dataMap {
+ model := reflect.New(fieldValue.Type().Elem()).Elem()
+
+ value, err := handleStruct(data, model)
+
+ if err != nil {
+ continue
+ }
+
+ models = reflect.Append(models, reflect.Indirect(value))
+ }
+
+ return models, nil
+}
diff --git a/vendor/github.com/google/jsonapi/response.go b/vendor/github.com/google/jsonapi/response.go
new file mode 100644
index 0000000..b44e4e9
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/response.go
@@ -0,0 +1,538 @@
+package jsonapi
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var (
+ // ErrBadJSONAPIStructTag is returned when the Struct field's JSON API
+ // annotation is invalid.
+ ErrBadJSONAPIStructTag = errors.New("Bad jsonapi struct tag format")
+ // ErrBadJSONAPIID is returned when the Struct JSON API annotated "id" field
+ // was not a valid numeric type.
+ ErrBadJSONAPIID = errors.New(
+ "id should be either string, int(8,16,32,64) or uint(8,16,32,64)")
+ // ErrExpectedSlice is returned when a variable or argument was expected to
+ // be a slice of *Structs; MarshalMany will return this error when its
+ // interface{} argument is invalid.
+ 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")
+)
+
+// MarshalPayload writes a jsonapi response for one or many records. The
+// related records are sideloaded into the "included" array. If this method is
+// given a struct pointer as an argument it will serialize in the form
+// "data": {...}. If this method is given a slice of pointers, this method will
+// serialize in the form "data": [...]
+//
+// One Example: you could pass it, w, your http.ResponseWriter, and, models, a
+// ptr to a Blog to be written to the response body:
+//
+// func ShowBlog(w http.ResponseWriter, r *http.Request) {
+// blog := &Blog{}
+//
+// 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 {
+ return err
+ }
+
+ return json.NewEncoder(w).Encode(payload)
+}
+
+// Marshal does the same as MarshalPayload except it just returns the payload
+// and doesn't write out results. Useful if you use your own JSON rendering
+// library.
+func Marshal(models interface{}) (Payloader, error) {
+ switch vals := reflect.ValueOf(models); vals.Kind() {
+ case reflect.Slice:
+ m, err := convertToSliceInterface(&models)
+ if err != nil {
+ return nil, err
+ }
+
+ payload, err := marshalMany(m)
+ if err != nil {
+ return nil, err
+ }
+
+ if linkableModels, isLinkable := models.(Linkable); isLinkable {
+ jl := linkableModels.JSONAPILinks()
+ if er := jl.validate(); er != nil {
+ return nil, er
+ }
+ payload.Links = linkableModels.JSONAPILinks()
+ }
+
+ if metableModels, ok := models.(Metable); ok {
+ payload.Meta = metableModels.JSONAPIMeta()
+ }
+
+ return payload, nil
+ 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
+ }
+}
+
+// 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 {
+ return err
+ }
+ payload.clearIncluded()
+
+ return json.NewEncoder(w).Encode(payload)
+}
+
+// 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) {
+ included := make(map[string]*Node)
+
+ rootNode, err := visitModelNode(model, &included, true)
+ if err != nil {
+ return nil, err
+ }
+ payload := &OnePayload{Data: rootNode}
+
+ payload.Included = nodeMapValues(&included)
+
+ return payload, 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
+// library.
+func marshalMany(models []interface{}) (*ManyPayload, error) {
+ payload := &ManyPayload{
+ Data: []*Node{},
+ }
+ included := map[string]*Node{}
+
+ for _, model := range models {
+ node, err := visitModelNode(model, &included, true)
+ if err != nil {
+ return nil, err
+ }
+ payload.Data = append(payload.Data, node)
+ }
+ payload.Included = nodeMapValues(&included)
+
+ 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 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 response. In other words, there
+// will be no, "included", array in the json all relationships will
+// be serailized inline in the data.
+//
+// However, in tests, you may want to construct payloads to post
+// to create methods that are embedded to most closely resemble
+// 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.
+func MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error {
+ rootNode, err := visitModelNode(model, nil, false)
+ if err != nil {
+ return err
+ }
+
+ payload := &OnePayload{Data: rootNode}
+
+ return json.NewEncoder(w).Encode(payload)
+}
+
+func visitModelNode(model interface{}, included *map[string]*Node,
+ sideload bool) (*Node, error) {
+ node := new(Node)
+
+ var er error
+ value := reflect.ValueOf(model)
+ if value.IsNil() {
+ return nil, nil
+ }
+
+ modelValue := value.Elem()
+ modelType := value.Type().Elem()
+
+ for i := 0; i < modelValue.NumField(); i++ {
+ structField := modelValue.Type().Field(i)
+ tag := structField.Tag.Get(annotationJSONAPI)
+ if tag == "" {
+ continue
+ }
+
+ fieldValue := modelValue.Field(i)
+ fieldType := modelType.Field(i)
+
+ args := strings.Split(tag, annotationSeperator)
+
+ if len(args) < 1 {
+ er = ErrBadJSONAPIStructTag
+ break
+ }
+
+ annotation := args[0]
+
+ if (annotation == annotationClientID && len(args) != 1) ||
+ (annotation != annotationClientID && len(args) < 2) {
+ er = ErrBadJSONAPIStructTag
+ break
+ }
+
+ if annotation == annotationPrimary {
+ v := fieldValue
+
+ // Deal with PTRS
+ var kind reflect.Kind
+ if fieldValue.Kind() == reflect.Ptr {
+ kind = fieldType.Type.Elem().Kind()
+ v = reflect.Indirect(fieldValue)
+ } else {
+ kind = fieldType.Type.Kind()
+ }
+
+ // Handle allowed types
+ switch kind {
+ case reflect.String:
+ node.ID = v.Interface().(string)
+ case reflect.Int:
+ node.ID = strconv.FormatInt(int64(v.Interface().(int)), 10)
+ case reflect.Int8:
+ node.ID = strconv.FormatInt(int64(v.Interface().(int8)), 10)
+ case reflect.Int16:
+ node.ID = strconv.FormatInt(int64(v.Interface().(int16)), 10)
+ case reflect.Int32:
+ node.ID = strconv.FormatInt(int64(v.Interface().(int32)), 10)
+ case reflect.Int64:
+ node.ID = strconv.FormatInt(v.Interface().(int64), 10)
+ case reflect.Uint:
+ node.ID = strconv.FormatUint(uint64(v.Interface().(uint)), 10)
+ case reflect.Uint8:
+ node.ID = strconv.FormatUint(uint64(v.Interface().(uint8)), 10)
+ case reflect.Uint16:
+ node.ID = strconv.FormatUint(uint64(v.Interface().(uint16)), 10)
+ case reflect.Uint32:
+ node.ID = strconv.FormatUint(uint64(v.Interface().(uint32)), 10)
+ case reflect.Uint64:
+ node.ID = strconv.FormatUint(v.Interface().(uint64), 10)
+ default:
+ // We had a JSON float (numeric), but our field was not one of the
+ // allowed numeric types
+ er = ErrBadJSONAPIID
+ }
+
+ if er != nil {
+ break
+ }
+
+ node.Type = args[1]
+ } else if annotation == annotationClientID {
+ clientID := fieldValue.String()
+ if clientID != "" {
+ node.ClientID = clientID
+ }
+ } else if annotation == annotationAttribute {
+ var omitEmpty, iso8601, rfc3339 bool
+
+ if len(args) > 2 {
+ for _, arg := range args[2:] {
+ switch arg {
+ case annotationOmitEmpty:
+ omitEmpty = true
+ case annotationISO8601:
+ iso8601 = true
+ case annotationRFC3339:
+ rfc3339 = true
+ }
+ }
+ }
+
+ if node.Attributes == nil {
+ node.Attributes = make(map[string]interface{})
+ }
+
+ if fieldValue.Type() == reflect.TypeOf(time.Time{}) {
+ t := fieldValue.Interface().(time.Time)
+
+ if t.IsZero() {
+ continue
+ }
+
+ if iso8601 {
+ node.Attributes[args[1]] = t.UTC().Format(iso8601TimeFormat)
+ } else if rfc3339 {
+ node.Attributes[args[1]] = t.UTC().Format(time.RFC3339)
+ } else {
+ node.Attributes[args[1]] = t.Unix()
+ }
+ } else if fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
+ // A time pointer may be nil
+ if fieldValue.IsNil() {
+ if omitEmpty {
+ continue
+ }
+
+ node.Attributes[args[1]] = nil
+ } else {
+ tm := fieldValue.Interface().(*time.Time)
+
+ if tm.IsZero() && omitEmpty {
+ continue
+ }
+
+ if iso8601 {
+ node.Attributes[args[1]] = tm.UTC().Format(iso8601TimeFormat)
+ } else if rfc3339 {
+ node.Attributes[args[1]] = tm.UTC().Format(time.RFC3339)
+ } else {
+ node.Attributes[args[1]] = tm.Unix()
+ }
+ }
+ } else {
+ // Dealing with a fieldValue that is not a time
+ emptyValue := reflect.Zero(fieldValue.Type())
+
+ // See if we need to omit this field
+ if omitEmpty && reflect.DeepEqual(fieldValue.Interface(), emptyValue.Interface()) {
+ continue
+ }
+
+ strAttr, ok := fieldValue.Interface().(string)
+ if ok {
+ node.Attributes[args[1]] = strAttr
+ } else {
+ node.Attributes[args[1]] = fieldValue.Interface()
+ }
+ }
+ } else if annotation == annotationRelation {
+ var omitEmpty bool
+
+ //add support for 'omitempty' struct tag for marshaling as absent
+ if len(args) > 2 {
+ omitEmpty = args[2] == annotationOmitEmpty
+ }
+
+ isSlice := fieldValue.Type().Kind() == reflect.Slice
+ if omitEmpty &&
+ (isSlice && fieldValue.Len() < 1 ||
+ (!isSlice && fieldValue.IsNil())) {
+ continue
+ }
+
+ if node.Relationships == nil {
+ node.Relationships = make(map[string]interface{})
+ }
+
+ var relLinks *Links
+ if linkableModel, ok := model.(RelationshipLinkable); ok {
+ relLinks = linkableModel.JSONAPIRelationshipLinks(args[1])
+ }
+
+ var relMeta *Meta
+ if metableModel, ok := model.(RelationshipMetable); ok {
+ relMeta = metableModel.JSONAPIRelationshipMeta(args[1])
+ }
+
+ if isSlice {
+ // to-many relationship
+ relationship, err := visitModelNodeRelationships(
+ fieldValue,
+ included,
+ sideload,
+ )
+ if err != nil {
+ er = err
+ break
+ }
+ relationship.Links = relLinks
+ relationship.Meta = relMeta
+
+ if sideload {
+ shallowNodes := []*Node{}
+ for _, n := range relationship.Data {
+ appendIncluded(included, n)
+ shallowNodes = append(shallowNodes, toShallowNode(n))
+ }
+
+ node.Relationships[args[1]] = &RelationshipManyNode{
+ Data: shallowNodes,
+ Links: relationship.Links,
+ Meta: relationship.Meta,
+ }
+ } else {
+ node.Relationships[args[1]] = relationship
+ }
+ } else {
+ // to-one relationships
+
+ // Handle null relationship case
+ if fieldValue.IsNil() {
+ node.Relationships[args[1]] = &RelationshipOneNode{Data: nil}
+ continue
+ }
+
+ relationship, err := visitModelNode(
+ fieldValue.Interface(),
+ included,
+ sideload,
+ )
+ if err != nil {
+ er = err
+ break
+ }
+
+ if sideload {
+ appendIncluded(included, relationship)
+ node.Relationships[args[1]] = &RelationshipOneNode{
+ Data: toShallowNode(relationship),
+ Links: relLinks,
+ Meta: relMeta,
+ }
+ } else {
+ node.Relationships[args[1]] = &RelationshipOneNode{
+ Data: relationship,
+ Links: relLinks,
+ Meta: relMeta,
+ }
+ }
+ }
+
+ } else {
+ er = ErrBadJSONAPIStructTag
+ break
+ }
+ }
+
+ if er != nil {
+ return nil, er
+ }
+
+ if linkableModel, isLinkable := model.(Linkable); isLinkable {
+ jl := linkableModel.JSONAPILinks()
+ if er := jl.validate(); er != nil {
+ return nil, er
+ }
+ node.Links = linkableModel.JSONAPILinks()
+ }
+
+ if metableModel, ok := model.(Metable); ok {
+ node.Meta = metableModel.JSONAPIMeta()
+ }
+
+ return node, nil
+}
+
+func toShallowNode(node *Node) *Node {
+ return &Node{
+ ID: node.ID,
+ Type: node.Type,
+ }
+}
+
+func visitModelNodeRelationships(models reflect.Value, included *map[string]*Node,
+ sideload bool) (*RelationshipManyNode, error) {
+ nodes := []*Node{}
+
+ for i := 0; i < models.Len(); i++ {
+ n := models.Index(i).Interface()
+
+ node, err := visitModelNode(n, included, sideload)
+ if err != nil {
+ return nil, err
+ }
+
+ nodes = append(nodes, node)
+ }
+
+ return &RelationshipManyNode{Data: nodes}, nil
+}
+
+func appendIncluded(m *map[string]*Node, nodes ...*Node) {
+ included := *m
+
+ for _, n := range nodes {
+ k := fmt.Sprintf("%s,%s", n.Type, n.ID)
+
+ if _, hasNode := included[k]; hasNode {
+ continue
+ }
+
+ included[k] = n
+ }
+}
+
+func nodeMapValues(m *map[string]*Node) []*Node {
+ mp := *m
+ nodes := make([]*Node, len(mp))
+
+ i := 0
+ for _, n := range mp {
+ nodes[i] = n
+ i++
+ }
+
+ return nodes
+}
+
+func convertToSliceInterface(i *interface{}) ([]interface{}, error) {
+ vals := reflect.ValueOf(*i)
+ if vals.Kind() != reflect.Slice {
+ return nil, ErrExpectedSlice
+ }
+ var response []interface{}
+ for x := 0; x < vals.Len(); x++ {
+ response = append(response, vals.Index(x).Interface())
+ }
+ return response, nil
+}
diff --git a/vendor/github.com/google/jsonapi/runtime.go b/vendor/github.com/google/jsonapi/runtime.go
new file mode 100644
index 0000000..db2d9f2
--- /dev/null
+++ b/vendor/github.com/google/jsonapi/runtime.go
@@ -0,0 +1,129 @@
+package jsonapi
+
+import (
+ "crypto/rand"
+ "fmt"
+ "io"
+ "reflect"
+ "time"
+)
+
+// Event represents a lifecycle event in the marshaling or unmarshalling
+// process.
+type Event int
+
+const (
+ // UnmarshalStart is the Event that is sent when deserialization of a payload
+ // begins.
+ UnmarshalStart Event = iota
+
+ // UnmarshalStop is the Event that is sent when deserialization of a payload
+ // ends.
+ UnmarshalStop
+
+ // MarshalStart is the Event that is sent sent when serialization of a payload
+ // begins.
+ MarshalStart
+
+ // MarshalStop is the Event that is sent sent when serialization of a payload
+ // ends.
+ MarshalStop
+)
+
+// Runtime has the same methods as jsonapi package for serialization and
+// deserialization but also has a ctx, a map[string]interface{} for storing
+// state, designed for instrumenting serialization timings.
+type Runtime struct {
+ ctx map[string]interface{}
+}
+
+// Events is the func type that provides the callback for handling event timings.
+type Events func(*Runtime, Event, string, time.Duration)
+
+// Instrumentation is a a global Events variable. This is the handler for all
+// timing events.
+var Instrumentation Events
+
+// NewRuntime creates a Runtime for use in an application.
+func NewRuntime() *Runtime { return &Runtime{make(map[string]interface{})} }
+
+// WithValue adds custom state variables to the runtime context.
+func (r *Runtime) WithValue(key string, value interface{}) *Runtime {
+ r.ctx[key] = value
+
+ return r
+}
+
+// Value returns a state variable in the runtime context.
+func (r *Runtime) Value(key string) interface{} {
+ return r.ctx[key]
+}
+
+// Instrument is deprecated.
+func (r *Runtime) Instrument(key string) *Runtime {
+ return r.WithValue("instrument", key)
+}
+
+func (r *Runtime) shouldInstrument() bool {
+ return Instrumentation != nil
+}
+
+// UnmarshalPayload has docs in request.go for UnmarshalPayload.
+func (r *Runtime) UnmarshalPayload(reader io.Reader, model interface{}) error {
+ return r.instrumentCall(UnmarshalStart, UnmarshalStop, func() error {
+ return UnmarshalPayload(reader, model)
+ })
+}
+
+// UnmarshalManyPayload has docs in request.go for UnmarshalManyPayload.
+func (r *Runtime) UnmarshalManyPayload(reader io.Reader, kind reflect.Type) (elems []interface{}, err error) {
+ r.instrumentCall(UnmarshalStart, UnmarshalStop, func() error {
+ elems, err = UnmarshalManyPayload(reader, kind)
+ return err
+ })
+
+ return
+}
+
+// MarshalPayload has docs in response.go for MarshalPayload.
+func (r *Runtime) MarshalPayload(w io.Writer, model interface{}) error {
+ return r.instrumentCall(MarshalStart, MarshalStop, func() error {
+ return MarshalPayload(w, model)
+ })
+}
+
+func (r *Runtime) instrumentCall(start Event, stop Event, c func() error) error {
+ if !r.shouldInstrument() {
+ return c()
+ }
+
+ instrumentationGUID, err := newUUID()
+ if err != nil {
+ return err
+ }
+
+ begin := time.Now()
+ Instrumentation(r, start, instrumentationGUID, time.Duration(0))
+
+ if err := c(); err != nil {
+ return err
+ }
+
+ diff := time.Duration(time.Now().UnixNano() - begin.UnixNano())
+ Instrumentation(r, stop, instrumentationGUID, diff)
+
+ return nil
+}
+
+// citation: http://play.golang.org/p/4FkNSiUDMg
+func newUUID() (string, error) {
+ uuid := make([]byte, 16)
+ if _, err := io.ReadFull(rand.Reader, uuid); err != nil {
+ return "", err
+ }
+ // variant bits; see section 4.1.1
+ uuid[8] = uuid[8]&^0xc0 | 0x80
+ // version 4 (pseudo-random); see section 4.1.3
+ uuid[6] = uuid[6]&^0xf0 | 0x40
+ return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
+}