summaryrefslogtreecommitdiff
path: root/vendor/github.com/google/jsonapi/README.md
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/README.md
parent05ca9b8d3a9c7203a3a3b590beaa400900bd9007 (diff)
chore: vendor go dependencies
Diffstat (limited to 'vendor/github.com/google/jsonapi/README.md')
-rw-r--r--vendor/github.com/google/jsonapi/README.md477
1 files changed, 477 insertions, 0 deletions
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*.