Back to godoc.org
github.com/derekdowling/go-json-spec-handler

Package jsh

v0.0.0-...-76701a9
Latest Go to latest

The highest tagged major version is .

Published: Jun 29, 2017 | License: MIT | Module: github.com/derekdowling/go-json-spec-handler

Overview

Package jsh (JSON API Specification Handler) makes it easy to parse JSON API requests and send responses that match the JSON API Specification: http://jsonapi.org/ from your server.

For a request client, see: jsc: https://godoc.org/github.com/derekdowling/go-json-spec-handler/client

For a full http.Handler API builder see jshapi: https://godoc.org/github.com/derekdowling/go-json-spec-handler/jsh-api

Index

Constants

const (
	// ContentType is the data encoding of choice for HTTP Request and Response Headers
	ContentType = "application/vnd.api+json"
)
const JSONAPIVersion = "1.1"

JSONAPIVersion is version of JSON API Spec that is currently compatible: http://jsonapi.org/format/1.1/

const MaxContentLength int64 = 10 << 20

MaxContentLength is 10MB https://github.com/golang/go/blob/abb3c0618b658a41bf91a087f1737412e93ff6d9/src/pkg/net/http/request.go#L617

Variables

var DefaultErrorDetail = "Request failed, something went wrong."

DefaultError can be customized in order to provide a more customized error Detail message when an Internal Server Error occurs. Optionally, you can modify a returned jsh.Error before sending it as a response as well.

var DefaultErrorTitle = "Internal Server Error"

DefaultTitle can be customized to provide a more customized ISE Title

var IncludeJSONAPIVersion = true

IncludeJSONAPIVersion is an option that allows consumers to include/remove the `jsonapi` top-level member from server responses.

func CreateReadCloser

func CreateReadCloser(data []byte) io.ReadCloser

CreateReadCloser is a helper function for dealing with creating HTTP requests

func NewObject

func NewObject(id string, resourceType string, attributes interface{}) (*Object, *Error)

NewObject prepares a new JSON Object for an API response. Whatever is provided as attributes will be marshalled to JSON.

func ParseDoc

func ParseDoc(r *http.Request, mode DocumentMode) (*Document, *Error)

ParseDoc parses and returns a top level jsh.Document. In most cases, using "ParseList" or "ParseObject" is preferable.

func ParseList

func ParseList(r *http.Request) (List, *Error)

ParseList validates the HTTP request and returns a resulting list of objects parsed from the request Body. Use just like ParseObject.

func ParseObject

func ParseObject(r *http.Request) (*Object, *Error)

ParseObject validates the HTTP request and returns a JSON object for a given io.ReadCloser containing a raw JSON payload. Here's an example of how to use it as part of your full flow.

func Handler(w http.ResponseWriter, r *http.Request) {
	obj, error := jsh.ParseObject(r)
	if error != nil {
		// log your error
		err := jsh.Send(w, r, error)
		return
	}

	yourType := &YourType{}

	err := object.Unmarshal("yourtype", &yourType)
	if err != nil {
		err := jsh.Send(w, r, err)
		return
	}

	yourType.ID = obj.ID
	// do business logic

	err := object.Marshal(yourType)
	if err != nil {
		// log error
		err := jsh.Send(w, r, err)
		return
	}

	err := jsh.Send(w, r, object)
}

type Document

type Document struct {
	Data List `json:"data"`
	// Object   *Object     `json:"-"`
	Errors   ErrorList   `json:"errors,omitempty"`
	Links    *Links      `json:"links,omitempty"`
	Included []*Object   `json:"included,omitempty"`
	Meta     interface{} `json:"meta,omitempty"`
	JSONAPI  *JSONAPI    `json:"jsonapi,omitempty"`
	// Status is an HTTP Status Code
	Status int `json:"-"`
	// DataMode to enforce for the document
	Mode DocumentMode `json:"-"`
	// contains filtered or unexported fields
}

Document represents a top level JSON formatted Document. Refer to the JSON API Specification for a full descriptor of each attribute: http://jsonapi.org/format/#document-structure

func Build

func Build(payload Sendable) *Document

Build creates a Sendable Document with the provided sendable payload, either Data or errors. Build also assumes you've already validated your data with .Validate() so it should be used carefully.

func New

func New() *Document

New instantiates a new JSON Document object.

func Ok

func Ok() *Document

Ok makes it simple to return a 200 OK response via jsh:

jsh.SendDocument(w, r, jsh.Ok())

func (*Document) AddError

func (d *Document) AddError(newErr *Error) *Error

AddError adds an error to the Document. It will also set the document Mode to "ErrorMode" if not done so already.

func (*Document) AddObject

func (d *Document) AddObject(object *Object) *Error

AddObject adds another object to the JSON Document after validating it.

func (*Document) Error

func (d *Document) Error() string

func (*Document) First

func (d *Document) First() *Object

First will return the first object from the document data if possible.

func (*Document) HasData

func (d *Document) HasData() bool

HasData will return true if the JSON document's Data field is set

func (*Document) HasErrors

func (d *Document) HasErrors() bool

HasErrors will return true if the Errors attribute is not nil.

func (*Document) MarshalJSON

func (d *Document) MarshalJSON() ([]byte, error)

MarshalJSON handles the custom serialization case caused by case where the "data" element of a document might be either a single resource object, or a collection of them.

func (*Document) Validate

func (d *Document) Validate(r *http.Request, isResponse bool) *Error

Validate performs document level checks against the JSONAPI specification. It is assumed that if this call returns without an error, your document is valid and can be sent as a request or response.

type DocumentMode

type DocumentMode int

DocumentMode allows different specification settings to be enforced based on the specified mode.

const (
	// ObjectMode enforces fetch request/response specifications
	ObjectMode DocumentMode = iota
	// ListMode enforces listing request/response specifications
	ListMode
	// ErrorMode enforces error response specifications
	ErrorMode
)

type Error

type Error struct {
	Title  string `json:"title"`
	Detail string `json:"detail"`
	Status int    `json:"status,string"`
	Source struct {
		Pointer string `json:"pointer"`
	} `json:"source"`
	ISE string `json:"-"`
}

Error consists of a number of contextual attributes to make conveying certain error type simpler as per the JSON API specification: http://jsonapi.org/format/#error-objects

error := &jsh.Error{
	Title: "Authentication Failure",
	Detail: "Category 4 Username Failure",
	Status: 401
}

jsh.Send(w, r, error)

func ISE

func ISE(internalMessage string) *Error

ISE is a convenience function for creating a ready-to-go Internal Service Error response. The message you pass in is set to the ErrorObject.ISE attribute so you can gracefully log ISE's internally before sending them.

func InputError

func InputError(msg string, attribute string) *Error

InputError creates a properly formatted HTTP Status 422 error with an appropriate user safe message. The parameter "attribute" will format err.Source.Pointer to be "/data/attributes/<attribute>".

func NotFound

func NotFound(resourceType string, id string) *Error

NotFound returns a 404 formatted error

func Send

func Send(w http.ResponseWriter, r *http.Request, payload Sendable) *Error

Send will return a JSON payload to the requestor. If the payload response validation fails, it will send an appropriate error to the requestor and will return the error

func SendDocument

func SendDocument(w http.ResponseWriter, r *http.Request, document *Document) *Error

SendDocument handles sending a fully prepared JSON Document. This is useful if you require custom validation or additional build steps before sending.

SendJSON is designed to always send a response, but will also return the last error it encountered to help with debugging in the event of an Internal Server Error.

func SpecificationError

func SpecificationError(detail string) *Error

SpecificationError is used whenever the Client violates the JSON API Spec

func (*Error) Error

func (e *Error) Error() string

Error will print an internal server error if set, or default back to the SafeError() format if not. As usual, err.Error() should not be considered safe for presentation to the end user, use err.SafeError() instead.

func (*Error) StatusCode

func (e *Error) StatusCode() int

StatusCode (HTTP) for the error. Defaults to 0.

func (*Error) Validate

func (e *Error) Validate(r *http.Request, response bool) *Error

Validate ensures that the an error meets all JSON API criteria.

type ErrorList

type ErrorList []*Error

ErrorList is wraps an Error Array so that it can implement Sendable

func (ErrorList) Error

func (e ErrorList) Error() string

Fulfills the default error interface

func (ErrorList) StatusCode

func (e ErrorList) StatusCode() int

StatusCode (HTTP) of the first error in the list. Defaults to 0 if the list is empty or one has not yet been set for the first error.

func (ErrorList) Validate

func (e ErrorList) Validate(r *http.Request, response bool) *Error

Validate checks all errors within the list to ensure that they are valid

type ErrorType

type ErrorType interface {
	// Error returns a formatted error and allows it to conform to the stdErr
	// interface.
	Error() string
	// Validate checks that the error is valid in the context of JSONAPI
	Validate(r *http.Request, response bool) *Error
	// StatusCode returns the first encountered HTTP Status Code for the error type.
	// Returns 0 if none is set.
	StatusCode() int
}

ErrorType represents the common interface requirements that libraries may specify if they would like to accept either a single error or a list.

type JSONAPI

type JSONAPI struct {
	Version string `json:"version"`
}

JSONAPI is the top-level member of a JSONAPI document that includes the server compatible version of the JSONAPI specification.

type Link struct {
	HREF string                 `json:"href,omitempty"`
	Meta map[string]interface{} `json:"meta,omitempty"`
}

Link is a resource link that can encode as a string or as an object as per the JSON API specification.

func NewLink(href string) *Link

NewLink creates a new link encoded as a string.

func NewMetaLink(href string, meta map[string]interface{}) *Link

NewMetaLink creates a new link with metadata encoded as an object.

func (*Link) MarshalJSON

func (l *Link) MarshalJSON() ([]byte, error)

MarshalJSON implements the Marshaler interface for Link.

func (*Link) UnmarshalJSON

func (l *Link) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the Unmarshaler interface for Link.

type Links struct {
	Self    *Link `json:"self,omitempty"`
	Related *Link `json:"related,omitempty"`
}

Links is a top-level document field

type List

type List []*Object

List is just a wrapper around an object array that implements Sendable

func (*List) UnmarshalJSON

func (list *List) UnmarshalJSON(rawData []byte) error

UnmarshalJSON allows us to manually decode a list via the json.Unmarshaler interface.

func (List) Validate

func (list List) Validate(r *http.Request, response bool) *Error

Validate ensures that List is JSON API compatible.

type Object

type Object struct {
	Type          string                   `json:"type" valid:"required"`
	ID            string                   `json:"id"`
	Attributes    json.RawMessage          `json:"attributes,omitempty"`
	Links         map[string]*Link         `json:"links,omitempty"`
	Relationships map[string]*Relationship `json:"relationships,omitempty"`
	Meta          map[string]interface{}   `json:"meta,omitempty"`
	// Status is the HTTP Status Code that should be associated with the object
	// when it is sent.
	Status int `json:"-"`
}

Object represents the default JSON spec for objects

func (*Object) Marshal

func (o *Object) Marshal(attributes interface{}) *Error

Marshal allows you to load a modified payload back into an object to preserve all of the data it has.

func (*Object) String

func (o *Object) String() string

String prints a formatted string representation of the object

func (*Object) Unmarshal

func (o *Object) Unmarshal(resourceType string, target interface{}) ErrorList

Unmarshal puts an Object's Attributes into a more useful target resourceType defined by the user. A correct object resourceType specified must also be provided otherwise an error is returned to prevent hard to track down situations.

Optionally, used https://github.com/go-validator/validator for request input validation. Simply define your struct with valid input tags:

struct {
	Username string `json:"username" valid:"required,alphanum"`
}

As the final action, the Unmarshal function will run govalidator on the unmarshal result. If the validator fails, a Sendable error response of HTTP Status 422 will be returned containing each validation error with a populated Error.Source.Pointer specifying each struct attribute that failed. In this case, all you need to do is:

errors := obj.Unmarshal("mytype", &myType)
if errors != nil {
	// log errors via error.ISE
	jsh.Send(w, r, errors)
}

func (*Object) Validate

func (o *Object) Validate(r *http.Request, response bool) *Error

Validate ensures that an object is JSON API compatible. Has a side effect of also setting the Object's Status attribute to be used as the Response HTTP Code if one has not already been set.

type Parser

type Parser struct {
	Method  string
	Headers http.Header
}

Parser is an abstraction layer that helps to support parsing JSON payload from many types of sources, and allows other libraries to leverage this if desired.

func NewParser

func NewParser(request *http.Request) *Parser

NewParser creates a parser from an http.Request

func (*Parser) Document

func (p *Parser) Document(payload io.ReadCloser, mode DocumentMode) (*Document, *Error)

Document returns a single JSON data object from the parser. In the process it will also validate any data objects against the JSON API.

type Relationship

type Relationship struct {
	Links *Links                 `json:"links,omitempty"`
	Data  ResourceLinkage        `json:"data,omitempty"`
	Meta  map[string]interface{} `json:"meta,omitempty"`
}

Relationship represents a reference from the resource object in which it's defined to other resource objects.

type ResourceIdentifier

type ResourceIdentifier struct {
	Type string `json:"type" valid:"required"`
	ID   string `json:"id" valid:"required"`
}

ResourceIdentifier identifies an individual resource.

type ResourceLinkage

type ResourceLinkage []*ResourceIdentifier

ResourceLinkage is a typedef around a slice of resource identifiers. This allows us to implement a custom UnmarshalJSON.

func (*ResourceLinkage) UnmarshalJSON

func (rl *ResourceLinkage) UnmarshalJSON(data []byte) error

UnmarshalJSON allows us to manually decode a the resource linkage via the json.Unmarshaler interface.

type Sendable

type Sendable interface {
	Validate(r *http.Request, response bool) *Error
}

Sendable implements functions that allows different response types to produce a sendable JSON Response format

Package Files

Documentation was rendered with GOOS=linux and GOARCH=amd64.

Jump to identifier

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to identifier