Documentation ¶
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/EtixLabs/go-json-spec-handler/client
For a full http.Handler API builder see jshapi: https://godoc.org/github.com/EtixLabs/jsh-api
Index ¶
- Constants
- Variables
- func AttributePointer(attribute string) string
- func CreateReadCloser(data []byte) io.ReadCloser
- func NewObject(id string, resourceType string, attributes interface{}) (*Object, *Error)
- func ParseDoc(r *http.Request, mode DocumentMode) (*Document, *Error)
- func ParseList(r *http.Request) (List, *Error)
- func ParseObject(r *http.Request) (*Object, *Error)
- func ParseRelationship(r *http.Request) (*IDObject, *Error)
- func ParseRelationshipList(r *http.Request) (IDList, *Error)
- func RelationshipPointer(relationship string) string
- type Document
- func (d *Document) AddError(newErr *Error) *Error
- func (d *Document) AddObject(object *Object) *Error
- func (d *Document) Error() string
- func (d *Document) First() *Object
- func (d *Document) HasData() bool
- func (d *Document) HasErrors() bool
- func (d *Document) MarshalJSON() ([]byte, error)
- func (d *Document) Validate(r *http.Request, isResponse bool) *Error
- type DocumentMode
- type Error
- func BadRequestError(msg string, detail string) *Error
- func ConflictError(resourceType string, id string) *Error
- func ForbiddenError(msg string) *Error
- func ISE(internalMessage string) *Error
- func InputError(msg string, attribute string) *Error
- func NotFound(resourceType string, id string) *Error
- func NotImplemented(internalMessage string) *Error
- func ParameterError(msg string, param string) *Error
- func RelationshipError(msg string, relationship string) *Error
- func Send(w http.ResponseWriter, r *http.Request, payload Sendable) *Error
- func SpecificationError(detail string) *Error
- func TopLevelError(field string) *Error
- type ErrorList
- type ErrorSource
- type ErrorType
- type IDList
- type IDObject
- type JSONAPI
- type Link
- type Links
- type List
- type Object
- func (o *Object) AddRelationshipLinks(name string)
- func (o *Object) AddRelationshipMany(name string, linkage IDList)
- func (o *Object) AddRelationshipOne(name string, linkage *IDObject)
- func (o *Object) AddSelfLink()
- func (o *Object) Marshal(attributes interface{}) *Error
- func (o *Object) ProcessCreate(resourceType string, model interface{}) ([]string, ErrorList)
- func (o *Object) ProcessUpdate(resourceType string, model interface{}) ([]string, ErrorList)
- func (o *Object) String() string
- func (o *Object) ToIDObject() *IDObject
- func (o *Object) Unmarshal(resourceType string, target interface{}) ErrorList
- func (o *Object) Validate(r *http.Request, response bool) *Error
- type Parser
- type Relationship
- type Sendable
- type Validator
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/
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.
Functions ¶
func AttributePointer ¶
AttributePointer returns a JSON pointer to the given attribute in a JSON API document.
func CreateReadCloser ¶
func CreateReadCloser(data []byte) io.ReadCloser
CreateReadCloser is a helper function for dealing with creating HTTP requests
func NewObject ¶
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 ¶
ParseList validates the HTTP request and returns a list of resource objects parsed from the request Body. Use just like ParseObject.
func ParseObject ¶
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) }
func ParseRelationship ¶
ParseRelationship validates the HTTP request and returns a relationship object. Use just like ParseObject.
func ParseRelationshipList ¶
ParseRelationshipList validates the HTTP request and returns a list of relationship objects parsed from the request Body. Use just like ParseList.
func RelationshipPointer ¶
RelationshipPointer returns a JSON pointer to the given primary resource relationship in a JSON API document.
Types ¶
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 ¶
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 Ok ¶
func Ok() *Document
Ok makes it simple to return a 200 OK response via jsh:
jsh.Send(w, r, jsh.Ok())
func (*Document) AddError ¶
AddError adds an error to the Document. It will also set the document Mode to "ErrorMode" if not done so already.
func (*Document) MarshalJSON ¶
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.
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 { Status int `json:"status,string"` Code string `json:"code,omitempty"` Title string `json:"title,omitempty"` Detail string `json:"detail,omitempty"` Source *ErrorSource `json:"source,omitempty"` 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 BadRequestError ¶
BadRequestError is a convenience function to return a 400 Bad Request response.
func ConflictError ¶
ConflictError returns a 409 Conflict error.
func ForbiddenError ¶
ForbiddenError is used whenever an attempt to do a forbidden operation is made.
func ISE ¶
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 ¶
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 NotImplemented ¶
NotImplemented is a convenience function similar to ISE except if generates a 501 response.
func ParameterError ¶
ParameterError creates a properly formatted HTTP Status 400 error with an appropriate user safe message. The err.Source.Parameter field will be set to the parameter "param".
func RelationshipError ¶
RelationshipError creates a properly formatted HTTP Status 422 error with an appropriate user safe message. The parameter "relationship" will format err.Source.Pointer to be "/data/relationship/<attribute>".
func Send ¶
Send will respond with the given JSON payload to the client. If the payload response validation fails, it will respond with the validation error and will return it. Send 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 ¶
SpecificationError returnss a 406 Not Acceptable. It is used whenever the Client violates the JSON API Spec.
func TopLevelError ¶
TopLevelError is used whenever the client sends a JSON payload with a missing top-level field.
func (*Error) Error ¶
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 ¶
StatusCode (HTTP) for the error. Defaults to 0.
type ErrorList ¶
type ErrorList []*Error
ErrorList is wraps an Error Array so that it can implement Sendable
func (ErrorList) StatusCode ¶
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.
type ErrorSource ¶
type ErrorSource struct { Pointer string `json:"pointer,omitempty"` Parameter string `json:"parameter,omitempty"` }
ErrorSource represents the source of a JSONAPI error, either by a pointer or a query parameter name.
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 IDList ¶
type IDList []*IDObject
IDList is a wrapper around a resource identifier slice that implements Sendable and Unmarshaler. IDList also implements sort.Interface for []*IDObject based on the ID field.
func (*IDList) UnmarshalJSON ¶
UnmarshalJSON allows us to manually decode a the resource linkage via the json.Unmarshaler interface.
type IDObject ¶
type IDObject struct { Type string `json:"type" valid:"required"` ID string `json:"id" valid:"required"` }
IDObject identifies an individual resource.
func NewIDObject ¶
NewIDObject creates a new resource identifier object instance.
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 ¶
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 NewMetaLink ¶
NewMetaLink creates a new link with metadata encoded as an object.
func NewRelationshipLink ¶
NewRelationshipLink creates a new relationship link encoded as a string.
func NewSelfLink ¶
NewSelfLink creates a new self link encoded as a string.
func (*Link) MarshalJSON ¶
MarshalJSON implements the Marshaler interface for Link.
func (*Link) UnmarshalJSON ¶
UnmarshalJSON implements the Unmarshaler interface for Link.
type Links ¶
Links is a top-level document field
func NewRelationshipLinks ¶
NewRelationshipLinks creates a new pair of relationship links encoded as a string.
type List ¶
type List []*Object
List is a wrapper around an object slice that implements Sendable. List implements sort.Interface for []*Object based on the ID field.
func (*List) UnmarshalJSON ¶
UnmarshalJSON allows us to manually decode a list via the json.Unmarshaler interface.
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) AddRelationshipLinks ¶
AddRelationshipLinks creates a new relationship link and adds it to the resource object relationships.
func (*Object) AddRelationshipMany ¶
AddRelationshipMany sets the resource linkage of the resource object for the given to-many relationship.
func (*Object) AddRelationshipOne ¶
AddRelationshipOne sets the resource linkage of the resource object for the given to-one relationship.
func (*Object) AddSelfLink ¶
func (o *Object) AddSelfLink()
AddSelfLink creates a new self link and adds it to the resource object links.
func (*Object) Marshal ¶
Marshal allows you to load a modified payload back into an object to preserve all of the data it has.
func (*Object) ProcessCreate ¶
ProcessCreate unmarshals the object to the given struct (see Object.Unmarshal) and uses JSH tags to validate that there is no missing attributes or forbidden ones.
Simply define your struct with jsh tags to allow for the model to be created with the tagged attributes.
struct { Username string `json:"username" jsh:"create"` }
You can also add a required option to ensure a specific attribute is non-zero.
struct { Username string `json:"username" jsh:"create/required"` }
The model must be a non-nil pointer to a struct. If valid, the model contains the valid request attributes after the call (even on validation error). Relationship fields, if any, are set to the IDObject values in object.Relationships.
See the documentation of Validator.Validate for more detailed information.
The string slice returned contains the names of the attributes and relationships that were unmarshaled to the model.
func (*Object) ProcessUpdate ¶
ProcessUpdate behaves just like ProcessCreate but uses the update tag for validation. It also adds the constraint of requiring at least one field to be updated.
func (*Object) ToIDObject ¶
ToIDObject returns a resource identifier object created with the object type and ID.
func (*Object) Unmarshal ¶
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) }
type Parser ¶
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 (*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 IDList `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 Sendable ¶
Sendable implements functions that allows different response types to produce a sendable JSON Response format
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator provides validation features for resource modeling.
func NewValidator ¶
NewValidator returns a new instance of a JSH validator.
func (*Validator) Validate ¶
Validate validates that the given struct has no missing/forbidden fields and relationships for the jsh action (i.e. create, update) according to JSH rules.
Customers must use JSH tags "create" and "update" to allow each field to be provided for create and update requests. An optional "/required" can be added to the tag to require the field to be provided.
Additionally, relationships fields must fulfill the following requirements:
- The field must be tagged "one" or "many".
- The JSON tag of the field should be "-" to prevent it from being included in attributes.
- If the relationship is tagged "one", the type of the field must be *jsh.IDObject.
- If the relationship is tagged "many", the type of the field must be either: map[string]*jsh.IDObject or map[int]*jsh.IDObject. The map must be non-nil.
Example model:
type User struct { Group *jsh.IDObject `json:"-" jsh:"one,create,update"` Name string `json:"username" jsh:"create/required"` Email string `json:"email" jsh:"create,update"` }
The given model should be the result of the unmarshaling of the internal object attributes. The validator will automatically update the relationship fields during validation.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package jsc (JSON Specification Client) is an http client that makes sending HTTP requests that match the JSON Specification: http://jsonapi.org/ simple.
|
Package jsc (JSON Specification Client) is an http client that makes sending HTTP requests that match the JSON Specification: http://jsonapi.org/ simple. |