rest

package
v0.0.0-...-55d3967 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2024 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package rest provides a framework that makes it easy to build a flexible and (mostly) unopinionated REST API with little ceremony. It offers tooling for creating stable, resource-oriented endpoints with fine-grained control over input and output fields. The go-rest framework is platform-agnostic, meaning it works both on- and off- App Engine, and pluggable in that it supports custom response serializers, middleware and authentication. It also includes a utility for generating API documentation.

Example (HelloWorld)

This example shows a minimal implementation of a ResourceHandler by using the BaseResourceHandler. It only implements an endpoint for fetching a resource.

package main

import "fmt"

// HelloWorldResource represents a domain model for which we want to perform
// CRUD operations with. Endpoints can operate on any type of entity --
// primitive, struct, or composite -- so long as it is serializable (by default,
// this means JSON-serializable via either MarshalJSON or JSON struct tags).
type HelloWorldResource struct {
	ID     int    `json:"id"`
	Foobar string `json:"foobar"`
}

// HelloWorldHandler implements the ResourceHandler interface. It specifies the
// business logic for performing CRUD operations. BaseResourceHandler provides
// stubs for each method if you only need to implement certain operations (as
// this example illustrates).
type HelloWorldHandler struct {
	BaseResourceHandler
}

// ResourceName is used to identify what resource a handler corresponds to and
// is used in the endpoint URLs, i.e. /api/:version/helloworld.
func (h HelloWorldHandler) ResourceName() string {
	return "helloworld"
}

// ReadResource is the logic that corresponds to reading a single resource by
// its ID at GET /api/:version/helloworld/{id}. Typically, this would make some
// sort of database query to load the resource. If the resource doesn't exist,
// nil should be returned along with an appropriate error.
func (h HelloWorldHandler) ReadResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	// Make a database call here.
	if id == "42" {
		return &HelloWorldResource{ID: 42, Foobar: "hello world"}, nil
	}
	return nil, ResourceNotFound(fmt.Sprintf("No resource with id %s", id))
}

// This example shows a minimal implementation of a ResourceHandler by using the
// BaseResourceHandler. It only implements an endpoint for fetching a resource.
func main() {
	api := NewAPI(NewConfiguration())

	// Call RegisterResourceHandler to wire up HelloWorldHandler.
	api.RegisterResourceHandler(HelloWorldHandler{})

	// We're ready to hit our CRUD endpoints.
	api.Start(":8080")
}
Output:

Example (IdsOnly)

This example shows how to implement a ResourceHandler which has an endpoint for fetching multiple resources and, optionally, retrieving only their IDs.

package main

import "strconv"

// MyResource represents a domain model for which we want to perform CRUD
// operations with. Endpoints can operate on any type of entity -- primitive,
// struct, or composite -- so long as it is serializable (by default, this means
// JSON-serializable via either MarshalJSON or JSON struct tags).
type MyResource struct {
	ID     int    `json:"id"`
	Foobar string `json:"foobar"`
}

// MyResourceHandler implements the ResourceHandler interface. It specifies the
// business logic for performing CRUD operations. BaseResourceHandler provides
// stubs for each method if you only need to implement certain operations (as
// this example illustrates).
type MyResourceHandler struct {
	BaseResourceHandler
}

// ResourceName is used to identify what resource a handler corresponds to and
// is used in the endpoint URLs, i.e. /api/:version/myresource.
func (m MyResourceHandler) ResourceName() string {
	return "myresource"
}

// ReadResourceList is the logic that corresponds to reading multiple resources,
// perhaps with specified query parameters accessed through the RequestContext.
// This is mapped to GET /api/:version/myresource. Typically, this would make
// some sort of database query to fetch the resources. It returns the slice of
// results, a cursor (or empty) string, and error (or nil). In this example, we
// illustrate how to use a query parameter to return only the IDs of our
// resources.
func (m MyResourceHandler) ReadResourceList(ctx RequestContext, limit int,
	cursor string, version string) ([]Resource, string, error) {
	// Make a database call here.
	resources := make([]Resource, 0, limit)
	resources = append(resources, &FooResource{ID: 1, Foobar: "hello"})
	resources = append(resources, &FooResource{ID: 2, Foobar: "world"})

	// ids_only is a query string parameter (i.e. /api/v1/myresource?ids_only=true).
	keysOnly, _ := strconv.ParseBool(ctx.ValueWithDefault("ids_only", "0").(string))

	if keysOnly {
		keys := make([]Resource, 0, len(resources))
		for _, resource := range resources {
			keys = append(keys, resource.(*FooResource).ID)
		}
		return keys, "", nil
	}

	return resources, "", nil
}

// This example shows how to implement a ResourceHandler which has an endpoint
// for fetching multiple resources and, optionally, retrieving only their IDs.
func main() {
	api := NewAPI(NewConfiguration())

	// Call RegisterResourceHandler to wire up HelloWorldHandler.
	api.RegisterResourceHandler(MyResourceHandler{})

	// We're ready to hit our CRUD endpoints.
	api.Start(":8080")
}
Output:

Example (Middleware)

This example shows how to implement request middleware. ResourceHandlers provide the Authenticate method which is used to authenticate requests, but middleware allows you to insert additional authorization, logging, or other AOP-style operations.

package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
)

// MiddlewareResource represents a domain model for which we want to perform
// CRUD operations with. Endpoints can operate on any type of entity --
// primitive, struct, or composite -- so long as it is serializable (by default,
// this means JSON-serializable via either MarshalJSON or JSON struct tags).
type MiddlewareResource struct {
	ID     int    `json:"id"`
	Foobar string `json:"foobar"`
}

// MiddlewareHandler implements the ResourceHandler interface. It specifies the
// business logic for performing CRUD operations. BaseResourceHandler provides
// stubs for each method if you only need to implement certain operations (as
// this example illustrates).
type MiddlewareHandler struct {
	BaseResourceHandler
}

// ResourceName is used to identify what resource a handler corresponds to and
// is used in the endpoint URLs, i.e. /api/:version/example.
func (e MiddlewareHandler) ResourceName() string {
	return "example"
}

// Authenticate is logic that is used to authenticate requests to this
// ResourceHandler. Returns nil if the request is authenticated or an error if
// it is not.
func (e MiddlewareHandler) Authenticate(r *http.Request) error {
	if r.Header.Get("Answer") != "42" {
		return errors.New("what is the answer?")
	}
	return nil
}

// ReadResource is the logic that corresponds to reading a single resource by
// its ID at GET /api/:version/example/{id}. Typically, this would make some
// sort of database query to load the resource. If the resource doesn't exist,
// nil should be returned along with an appropriate error.
func (e MiddlewareHandler) ReadResource(ctx RequestContext, id string, version string) (Resource, error) {
	// Make a database call here.
	if id == "42" {
		return &MiddlewareResource{ID: 42, Foobar: "hello world"}, nil
	}
	return nil, ResourceNotFound(fmt.Sprintf("No resource with id %s", id))
}

// ResourceHandler middleware is implemented as a closure which takes an
// http.Handler and returns one.
func HandlerMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("Request: %s", r.URL.String())
		next.ServeHTTP(w, r)
	})
}

// Global API middleware is implemented as a function which takes an
// http.ResponseWriter and http.Request and returns a MiddlewareError if the
// request should terminate.
func GlobalMiddleware(w http.ResponseWriter, r *http.Request) *MiddlewareError {
	log.Println(r)
	return nil
}

// This example shows how to implement request middleware. ResourceHandlers
// provide the Authenticate method which is used to authenticate requests, but
// middleware allows you to insert additional authorization, logging, or other
// AOP-style operations.
func main() {
	api := NewAPI(NewConfiguration())

	// Call RegisterResourceHandler to wire up MiddlewareHandler and apply middleware.
	api.RegisterResourceHandler(MiddlewareHandler{}, HandlerMiddleware)

	// Middleware provided to Start and StartTLS are invoked for every request handled
	// by the API.
	api.Start(":8080", GlobalMiddleware)
}
Output:

Example (ResponseSerializer)

This example shows how to implement a custom ResponseSerializer. The format responses are sent in is specified by the "format" query string parameter. By default, json is the only available format, but the ResponseSerializer interface allows different formats to be implemented.

package main

import "gopkg.in/yaml.v1"

// YAMLSerializer implements the ResponseSerializer interface.
type YAMLSerializer struct{}

// Serialize marshals a response payload into a byte slice to be sent over the
// wire.
func (y YAMLSerializer) Serialize(p Payload) ([]byte, error) {
	return yaml.Marshal(p)
}

// ContentType returns the MIME type of the response.
func (y YAMLSerializer) ContentType() string {
	return "text/yaml"
}

// This example shows how to implement a custom ResponseSerializer. The format
// responses are sent in is specified by the "format" query string parameter. By
// default, json is the only available format, but the ResponseSerializer
// interface allows different formats to be implemented.
func main() {
	api := NewAPI(NewConfiguration())

	// Call RegisterResponseSerializer to wire up YAMLSerializer.
	api.RegisterResponseSerializer("yaml", YAMLSerializer{})

	// Call RegisterResourceHandler to wire up HelloWorldHandler.
	api.RegisterResourceHandler(HelloWorldHandler{})

	// We're ready to hit our CRUD endpoints. Use ?format=yaml to get responses as YAML.
	api.Start(":8080")
}
Output:

Example (Rules)

This example shows how Rules are used to provide fine-grained control over response input and output.

package main

import (
	"fmt"
	"math/rand"
)

// ResourceWithSecret represents a domain model for which we want to perform
// CRUD operations with. Endpoints can operate on any type of entity --
// primitive, struct, or composite -- so long as it is serializable (by default,
// this means JSON-serializable via either MarshalJSON or JSON struct tags). The
// resource in this example has a field, "Secret", which we don't want to
// include in REST responses.
type ResourceWithSecret struct {
	ID     int
	Foo    string
	Nested FooResource
	Secret string
}

// ResourceWithSecretHandler implements the ResourceHandler interface. It
// specifies the business logic for performing CRUD operations.
// BaseResourceHandler provides stubs for each method if you only need to
// implement certain operations (as this example illustrates).
type ResourceWithSecretHandler struct {
	BaseResourceHandler
}

// ResourceName is used to identify what resource a handler corresponds to and
// is used in the endpoint URLs, i.e. /api/:version/resource.
func (r ResourceWithSecretHandler) ResourceName() string {
	return "resource"
}

// CreateResource is the logic that corresponds to creating a new resource at
// POST /api/:version/resource. Typically, this would insert a record into a
// database. It returns the newly created resource or an error if the create
// failed. Because our Rules specify types, we can access the Payload data in a
// type-safe way.
func (r ResourceWithSecretHandler) CreateResource(ctx RequestContext, data Payload,
	version string) (Resource, error) {
	// Make a database call here.
	id := rand.Int()
	foo, _ := data.GetString("foo")
	created := &ResourceWithSecret{ID: id, Foo: foo, Secret: "secret"}
	return created, nil
}

// ReadResource is the logic that corresponds to reading a single resource by
// its ID at GET /api/:version/resource/{id}. Typically, this would make some
// sort of database query to load the resource. If the resource doesn't exist,
// nil should be returned along with an appropriate error.
func (r ResourceWithSecretHandler) ReadResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	// Make a database call here.
	if id == "42" {
		return &ResourceWithSecret{
			ID:     42,
			Foo:    "hello world",
			Secret: "keep it secret, keep it safe",
		}, nil
	}
	return nil, ResourceNotFound(fmt.Sprintf("No resource with id %s", id))
}

// Rules returns the resource rules to apply to incoming requests and outgoing
// responses. The default behavior, seen in BaseResourceHandler, is to apply no
// rules. Note that a Rule is not specified for the "Secret" field. This means
// that field will not be included in the response. The "Type" field on a Rule
// indicates the type the incoming data should be coerced to. If coercion fails,
// an error indicating this will be sent back in the response. If no type is
// specified, no coercion will be performed. Rules may also be nested. NewRules
// is used to initialize Rules and associate them with the resource type using
// the nil pointer. This allows Rule validation to occur at startup.
func (r ResourceWithSecretHandler) Rules() Rules {
	return NewRules((*ResourceWithSecret)(nil),
		&Rule{
			Field:      "ID",
			FieldAlias: "id",
			Type:       Int,
			Versions:   []string{"1, 2"},
			OutputOnly: true,
		},
		&Rule{
			Field:      "Foo",
			FieldAlias: "f",
			Type:       String,
			Versions:   []string{"1"},
			Required:   true,
		},
		&Rule{
			Field:      "Foo",
			FieldAlias: "foo",
			Type:       String,
			Versions:   []string{"2"},
			Required:   true,
		},
		&Rule{
			Field:      "Nested",
			FieldAlias: "nested",
			Versions:   []string{"2"},
			Rules: NewRules((*FooResource)(nil),
				&Rule{
					Field:      "ID",
					FieldAlias: "id",
					Type:       Int,
					OutputOnly: true,
				},
				&Rule{
					Field:      "Foobar",
					FieldAlias: "foobar",
					Type:       String,
				},
			),
		},
	)
}

// This example shows how Rules are used to provide fine-grained control over
// response input and output.
func main() {
	api := NewAPI(NewConfiguration())

	// Call RegisterResourceHandler to wire up ResourceWithSecretHandler.
	api.RegisterResourceHandler(ResourceWithSecretHandler{})

	// We're ready to hit our CRUD endpoints.
	api.Start(":8080")
}
Output:

Example (SimpleCrud)

This example shows how to fully implement a basic ResourceHandler for performing CRUD operations.

package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
)

// FooResource represents a domain model for which we want to perform CRUD
// operations with. Endpoints can operate on any type of entity -- primitive,
// struct, or composite -- so long as it is serializable (by default, this means
// JSON-serializable via either MarshalJSON or JSON struct tags).
type FooResource struct {
	ID     int    `json:"id"`
	Foobar string `json:"foobar"`
}

// FooHandler implements the ResourceHandler interface. It specifies the
// business logic for performing CRUD operations.
type FooHandler struct {
	BaseResourceHandler
}

// ResourceName is used to identify what resource a handler corresponds to and
// is used in the endpoint URLs, i.e. /api/:version/foo.
func (f FooHandler) ResourceName() string {
	return "foo"
}

// CreateResource is the logic that corresponds to creating a new resource at
// POST /api/:version/foo. Typically, this would insert a record into a
// database. It returns the newly created resource or an error if the create
// failed.
func (f FooHandler) CreateResource(ctx RequestContext, data Payload,
	version string) (Resource, error) {
	// Make a database call here.
	id := rand.Int()
	foobar, _ := data.GetString("foobar")
	created := &FooResource{ID: id, Foobar: foobar}
	return created, nil
}

// ReadResource is the logic that corresponds to reading a single resource by
// its ID at GET /api/:version/foo/{id}. Typically, this would make some sort of
// database query to load the resource. If the resource doesn't exist, nil
// should be returned along with an appropriate error.
func (f FooHandler) ReadResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	// Make a database call here.
	if id == "42" {
		return &FooResource{ID: 42, Foobar: "hello world"}, nil
	}
	return nil, ResourceNotFound(fmt.Sprintf("No resource with id %s", id))
}

// ReadResourceList is the logic that corresponds to reading multiple resources,
// perhaps with specified query parameters accessed through the RequestContext.
// This is mapped to GET /api/:version/foo. Typically, this would make some sort
// of database query to fetch the resources. It returns the slice of results, a
// cursor (or empty) string, and error (or nil).
func (f FooHandler) ReadResourceList(ctx RequestContext, limit int,
	cursor string, version string) ([]Resource, string, error) {
	// Make a database call here.
	resources := make([]Resource, 0, limit)
	resources = append(resources, &FooResource{ID: 1, Foobar: "hello"})
	resources = append(resources, &FooResource{ID: 2, Foobar: "world"})
	return resources, "", nil
}

// UpdateResource is the logic that corresponds to updating an existing resource
// at PUT /api/:version/foo/{id}. Typically, this would make some sort of
// database update call. It returns the updated resource or an error if the
// update failed.
func (f FooHandler) UpdateResource(ctx RequestContext, id string, data Payload,
	version string) (Resource, error) {
	// Make a database call here.
	updateID, _ := strconv.Atoi(id)
	foobar, _ := data.GetString("foobar")
	foo := &FooResource{ID: updateID, Foobar: foobar}
	return foo, nil
}

// DeleteResource is the logic that corresponds to deleting an existing resource
// at DELETE /api/:version/foo/{id}. Typically, this would make some sort of
// database delete call. It returns the deleted resource or an error if the
// delete failed.
func (f FooHandler) DeleteResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	// Make a database call here.
	deleteID, _ := strconv.Atoi(id)
	foo := &FooResource{ID: deleteID, Foobar: "Goodbye world"}
	return foo, nil
}

// Authenticate is logic that is used to authenticate requests. The default
// behavior of Authenticate, seen in BaseResourceHandler, always returns nil,
// meaning all requests are authenticated. Returning an error means that the
// request is unauthorized and any error message will be sent back with the
// response.
func (f FooHandler) Authenticate(r *http.Request) error {
	if secrets, ok := r.Header["Authorization"]; ok {
		if secrets[0] == "secret" {
			return nil
		}
	}

	return UnauthorizedRequest("You shall not pass")
}

// This example shows how to fully implement a basic ResourceHandler for
// performing CRUD operations.
func main() {
	api := NewAPI(NewConfiguration())

	// Call RegisterResourceHandler to wire up FooHandler.
	api.RegisterResourceHandler(FooHandler{})

	// We're ready to hit our CRUD endpoints.
	api.Start(":8080")
}
Output:

Index

Examples

Constants

View Source
const (

	// Handler names
	HandleCreate     HandleMethod = "create"
	HandleRead                    = "read"
	HandleUpdate                  = "update"
	HandleDelete                  = "delete"
	HandleReadList                = "readList"
	HandleUpdateList              = "updateList"
)

Variables

This section is empty.

Functions

func NewResponse

func NewResponse(ctx RequestContext) response

NewResponse constructs a new response struct containing the payload to send back. It will either be a success or error response depending on the RequestContext.

Types

type API

type API interface {
	http.Handler

	// Start begins serving requests. This will block unless it fails, in which case an
	// error will be returned. This will validate any defined Rules. If any Rules are
	// invalid, it will panic. Any provided Middleware will be invoked for every request
	// handled by the API.
	Start(Address, ...Middleware) error

	// StartTLS begins serving requests received over HTTPS connections. This will block
	// unless it fails, in which case an error will be returned. Files containing a
	// certificate and matching private key for the server must be provided. If the
	// certificate is signed by a certificate authority, the certFile should be the
	// concatenation of the server's certificate followed by the CA's certificate. This
	// will validate any defined Rules. If any Rules are invalid, it will panic. Any
	// provided Middleware will be invoked for every request handled by the API.
	StartTLS(Address, FilePath, FilePath, ...Middleware) error

	// RegisterResourceHandler binds the provided ResourceHandler to the appropriate REST
	// endpoints and applies any specified middleware. Endpoints will have the following
	// base URL: /api/:version/resourceName.
	RegisterResourceHandler(ResourceHandler, ...RequestMiddleware)

	// RegisterHandlerFunc binds the http.HandlerFunc to the provided URI and applies any
	// specified middleware.
	RegisterHandlerFunc(string, http.HandlerFunc, ...RequestMiddleware)

	// RegisterHandler binds the http.Handler to the provided URI and applies any specified
	// middleware.
	RegisterHandler(string, http.Handler, ...RequestMiddleware)

	// RegisterPathPrefix binds the http.HandlerFunc to URIs matched by the given path
	// prefix and applies any specified middleware.
	RegisterPathPrefix(string, http.HandlerFunc, ...RequestMiddleware)

	// RegisterResponseSerializer registers the provided ResponseSerializer with the given
	// format. If the format has already been registered, it will be overwritten.
	RegisterResponseSerializer(string, ResponseSerializer)

	// UnregisterResponseSerializer unregisters the ResponseSerializer with the provided
	// format. If the format hasn't been registered, this is a no-op.
	UnregisterResponseSerializer(string)

	// AvailableFormats returns a slice containing all of the available serialization
	// formats currently available.
	AvailableFormats() []string

	// Configuration returns the API Configuration.
	Configuration() *Configuration

	// ResourceHandlers returns a slice containing the registered ResourceHandlers.
	ResourceHandlers() []ResourceHandler

	// Validate will validate the Rules configured for this API. It returns nil
	// if all Rules are valid, otherwise returns the first encountered
	// validation error.
	Validate() error
	// contains filtered or unexported methods
}

API is the top-level interface encapsulating an HTTP REST server. It's responsible for registering ResourceHandlers and routing requests. Use NewAPI to retrieve an instance.

func NewAPI

func NewAPI(config *Configuration) API

NewAPI returns a newly allocated API instance.

type Address

type Address string

Address is the address and port to bind to (e.g. ":8080").

type BaseResourceHandler

type BaseResourceHandler struct{}

BaseResourceHandler is a base implementation of ResourceHandler with stubs for the CRUD operations. This allows ResourceHandler implementations to only implement what they need.

func (BaseResourceHandler) Authenticate

func (b BaseResourceHandler) Authenticate(r *http.Request) error

Authenticate is the default authentication logic. All requests are authorized. Implement custom authentication logic if necessary.

func (BaseResourceHandler) CreateDocumentation

func (b BaseResourceHandler) CreateDocumentation() string

CreateDocumentation is a stub. Implement if necessary.

func (BaseResourceHandler) CreateResource

func (b BaseResourceHandler) CreateResource(ctx RequestContext, data Payload,
	version string) (Resource, error)

CreateResource is a stub. Implement if necessary.

func (BaseResourceHandler) CreateURI

func (b BaseResourceHandler) CreateURI() string

CreateURI is a stub. Implement if necessary. The default create URI is /api/v{version:[^/]+}/resourceName.

func (BaseResourceHandler) DeleteDocumentation

func (b BaseResourceHandler) DeleteDocumentation() string

DeleteDocumentation is a stub. Implement if necessary.

func (BaseResourceHandler) DeleteResource

func (b BaseResourceHandler) DeleteResource(ctx RequestContext, id string,
	version string) (Resource, error)

DeleteResource is a stub. Implement if necessary.

func (BaseResourceHandler) DeleteURI

func (b BaseResourceHandler) DeleteURI() string

DeleteURI is a stub. Implement if necessary. The default delete URI is /api/v{version:[^/]+}/resourceName/{resource_id}.

func (BaseResourceHandler) ReadDocumentation

func (b BaseResourceHandler) ReadDocumentation() string

ReadDocumentation is a stub. Implement if necessary.

func (BaseResourceHandler) ReadListDocumentation

func (b BaseResourceHandler) ReadListDocumentation() string

ReadListDocumentation is a stub. Implement if necessary.

func (BaseResourceHandler) ReadListURI

func (b BaseResourceHandler) ReadListURI() string

ReadListURI is a stub. Implement if necessary. The default read list URI is /api/v{version:[^/]+}/resourceName.

func (BaseResourceHandler) ReadResource

func (b BaseResourceHandler) ReadResource(ctx RequestContext, id string,
	version string) (Resource, error)

ReadResource is a stub. Implement if necessary.

func (BaseResourceHandler) ReadResourceList

func (b BaseResourceHandler) ReadResourceList(ctx RequestContext, limit int,
	cursor string, version string) ([]Resource, string, error)

ReadResourceList is a stub. Implement if necessary.

func (BaseResourceHandler) ReadURI

func (b BaseResourceHandler) ReadURI() string

ReadURI is a stub. Implement if necessary. The default read URI is /api/v{version:[^/]+}/resourceName/{resource_id}.

func (BaseResourceHandler) ResourceName

func (b BaseResourceHandler) ResourceName() string

ResourceName is a stub. It must be implemented.

func (BaseResourceHandler) Rules

func (b BaseResourceHandler) Rules() Rules

Rules returns the resource rules to apply to incoming requests and outgoing responses. No rules are applied by default. Implement if necessary.

func (BaseResourceHandler) UpdateDocumentation

func (b BaseResourceHandler) UpdateDocumentation() string

UpdateDocumentation is a stub. Implement if necessary.

func (BaseResourceHandler) UpdateListDocumentation

func (b BaseResourceHandler) UpdateListDocumentation() string

UpdateListDocumentation is a stub. Implement if necessary.

func (BaseResourceHandler) UpdateListURI

func (b BaseResourceHandler) UpdateListURI() string

UpdateListURI is a stub. Implement if necessary. The default update list URI is /api/v{version:[^/]+}/resourceName.

func (BaseResourceHandler) UpdateResource

func (b BaseResourceHandler) UpdateResource(ctx RequestContext, id string,
	data Payload, version string) (Resource, error)

UpdateResource is a stub. Implement if necessary.

func (BaseResourceHandler) UpdateResourceList

func (b BaseResourceHandler) UpdateResourceList(ctx RequestContext, data []Payload,
	version string) ([]Resource, error)

UpdateResourceList is a stub. Implement if necessary.

func (BaseResourceHandler) UpdateURI

func (b BaseResourceHandler) UpdateURI() string

UpdateURI is a stub. Implement if necessary. The default update URI is /api/v{version:[^/]+}/resourceName/{resource_id}.

func (BaseResourceHandler) ValidVersions

func (b BaseResourceHandler) ValidVersions() []string

type Client

type Client struct {
	HttpClient
}

Client is the type that encapsulates and uses the Authorizer to sign any REST requests that are performed.

func (*Client) Delete

func (c *Client) Delete(url string, header http.Header) (*Response, error)

Delete will perform an HTTP DELETE on the specified URL and return the response.

func (*Client) Get

func (c *Client) Get(url string, header http.Header) (*Response, error)

Get will perform an HTTP GET on the specified URL and return the response.

func (*Client) Post

func (c *Client) Post(url string, body interface{}, header http.Header) (*Response, error)

Post will perform an HTTP POST on the specified URL and return the response.

func (*Client) Put

func (c *Client) Put(url string, body interface{}, header http.Header) (*Response, error)

Put will perform an HTTP PUT on the specified URL and return the response.

type ClientMiddleware

type ClientMiddleware func(InvocationHandler) InvocationHandler

ClientMiddleware is a function that wraps another function, and returns the wrapped function

type Configuration

type Configuration struct {
	Debug         bool
	Logger        StdLogger
	GenerateDocs  bool
	DocsDirectory string
}

Configuration contains settings for configuring an API.

func NewConfiguration

func NewConfiguration() *Configuration

NewConfiguration returns a default Configuration.

func (*Configuration) Debugf

func (c *Configuration) Debugf(format string, v ...interface{})

Debugf prints the formatted string to the Configuration Logger if Debug is enabled.

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error is an implementation of the error interface representing an HTTP error.

func BadRequest

func BadRequest(reason string) Error

BadRequest returns a Error for a 400 Bad Request error.

func CustomError

func CustomError(reason string, status int) Error

CustomError returns an Error for the given HTTP status code.

func InternalServerError

func InternalServerError(reason string) Error

InternalServerError returns a Error for a 500 Internal Server error.

func MethodNotAllowed

func MethodNotAllowed(reason string) Error

MethodNotAllowed returns a Error for a 405 Method Not Allowed error.

func ResourceConflict

func ResourceConflict(reason string) Error

ResourceConflict returns a Error for a 409 Conflict error.

func ResourceNotFound

func ResourceNotFound(reason string) Error

ResourceNotFound returns a Error for a 404 Not Found error.

func ResourceNotPermitted

func ResourceNotPermitted(reason string) Error

ResourceNotPermitted returns a Error for a 403 Forbidden error.

func UnauthorizedRequest

func UnauthorizedRequest(reason string) Error

UnauthorizedRequest returns a Error for a 401 Unauthorized error.

func UnprocessableRequest

func UnprocessableRequest(reason string) Error

UnprocessableRequest returns a Error for a 422 Unprocessable Entity error.

func (Error) Error

func (r Error) Error() string

Error returns the Error message.

func (Error) Status

func (r Error) Status() int

Status returns the HTTP status code.

type FilePath

type FilePath string

FilePath represents a file path.

type Filter

type Filter bool

Filter is a category for filtering Rules.

const (
	Inbound  Filter = true
	Outbound Filter = false
)

Rule category Filters.

type HandleMethod

type HandleMethod string

type HttpClient

type HttpClient interface {
	Do(req *http.Request) (resp *http.Response, err error)
	Get(url string) (resp *http.Response, err error)
	Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error)
	PostForm(url string, data url.Values) (resp *http.Response, err error)
	Head(url string) (resp *http.Response, err error)
}

HttpClient is the type that is used to perform HTTP Methods

type InvocationHandler

type InvocationHandler func(c *http.Client, method, url string, body interface{}, header http.Header) (*Response, error)

InvocationHandler is a function that is to be wrapped by the ClientMiddleware

type Middleware

type Middleware func(w http.ResponseWriter, r *http.Request) *MiddlewareError

Middleware can be passed in to API#Start and API#StartTLS and will be invoked on every request to a route handled by the API. Returns a MiddlewareError if the request should be terminated.

type MiddlewareError

type MiddlewareError struct {
	Code     int
	Response []byte
}

MiddlewareError is returned by Middleware to indicate that a request should not be served.

type Payload

type Payload map[string]interface{}

Payload is the unmarshalled request body.

func (Payload) Get

func (p Payload) Get(key string) (interface{}, error)

Get returns the value with the given key as an interface{}. If the key doesn't exist, nil is returned with an error.

func (Payload) GetBool

func (p Payload) GetBool(key string) (bool, error)

GetBool returns the value with the given key as a bool. If the key doesn't exist or is not a bool, false is returned with an error.

func (Payload) GetByte

func (p Payload) GetByte(key string) (byte, error)

GetByte returns the value with the given key as a byte. If the key doesn't exist or is not a byte, the zero value is returned with an error.

func (Payload) GetDuration

func (p Payload) GetDuration(key string) (time.Duration, error)

GetDuration returns the value with the given key as a time.Duration. If the key doesn't exist or is not a time.Duration, the zero value is returned with an error.

func (Payload) GetFloat32

func (p Payload) GetFloat32(key string) (float32, error)

GetFloat32 returns the value with the given key as a Float32. If the key doesn't exist or is not a Float32, the zero value is returned with an error.

func (Payload) GetFloat64

func (p Payload) GetFloat64(key string) (float64, error)

GetFloat64 returns the value with the given key as a Float64. If the key doesn't exist or is not a Float32, the zero value is returned with an error.

func (Payload) GetInt

func (p Payload) GetInt(key string) (int, error)

GetInt returns the value with the given key as an int. If the key doesn't exist or is not an int, the zero value is returned with an error.

func (Payload) GetInt16

func (p Payload) GetInt16(key string) (int16, error)

GetInt16 returns the value with the given key as an int16. If the key doesn't exist or is not an int16, the zero value is returned with an error.

func (Payload) GetInt32

func (p Payload) GetInt32(key string) (int32, error)

GetInt32 returns the value with the given key as an int32. If the key doesn't exist or is not an int32, the zero value is returned with an error.

func (Payload) GetInt64

func (p Payload) GetInt64(key string) (int64, error)

GetInt64 returns the value with the given key as an int64. If the key doesn't exist or is not an int64, the zero value is returned with an error.

func (Payload) GetInt8

func (p Payload) GetInt8(key string) (int8, error)

GetInt8 returns the value with the given key as an int8. If the key doesn't exist or is not an int8, the zero value is returned with an error.

func (Payload) GetMap

func (p Payload) GetMap(key string) (map[string]interface{}, error)

GetMap returns the value with the given key as a map[string]interface{}. If the key doesn't exist or is not a map[string]interface{}, nil is returned with an error.

func (Payload) GetSlice

func (p Payload) GetSlice(key string) ([]interface{}, error)

GetSlice returns the value with the given key as an []interface{}. If the value doesn't exist or is not an []interface{}, nil is returned with an error.

func (Payload) GetString

func (p Payload) GetString(key string) (string, error)

GetString returns the value with the given key as a string. If the key doesn't exist or is not a string, the zero value is returned with an error.

func (Payload) GetTime

func (p Payload) GetTime(key string) (time.Time, error)

GetTime returns the value with the given key as a time.Time. If the key doesn't exist or is not a time.Time, the zero value is returned with an error.

func (Payload) GetUint

func (p Payload) GetUint(key string) (uint, error)

GetUint returns the value with the given key as a uint. If the key doesn't exist or is not a uint, the zero value is returned with an error.

func (Payload) GetUint16

func (p Payload) GetUint16(key string) (uint16, error)

GetUint16 returns the value with the given key as a uint16. If the key doesn't exist or is not a uint16, the zero value is returned with an error.

func (Payload) GetUint32

func (p Payload) GetUint32(key string) (uint32, error)

GetUint32 returns the value with the given key as a uint32. If the key doesn't exist or is not a uint32, the zero value is returned with an error.

func (Payload) GetUint64

func (p Payload) GetUint64(key string) (uint64, error)

GetUint64 returns the value with the given key as a uint64. If the key doesn't exist or is not a uint64, the zero value is returned with an error.

func (Payload) GetUint8

func (p Payload) GetUint8(key string) (uint8, error)

GetUint8 returns the value with the given key as a uint8. If the key doesn't exist or is not a uint8, the zero value is returned with an error.

type RequestContext

type RequestContext interface {
	// Value returns the value associated with this context for key, or nil
	// if no value is associated with key. Successive calls to Value with
	// the same key returns the same result.
	Value(key interface{}) interface{}

	// WithValue returns a new RequestContext with the provided key-value pair and this context
	// as the parent.
	WithValue(interface{}, interface{}) RequestContext

	// ValueWithDefault returns the context value for the given key. If there's no such value,
	// the provided default is returned.
	ValueWithDefault(interface{}, interface{}) interface{}

	// Request returns the *http.Request associated with context using NewContext, if any.
	Request() (*http.Request, bool)

	// NextURL returns the URL to use to request the next page of results using the current
	// cursor. If there is no cursor for this request or the URL fails to be built, an empty
	// string is returned with the error set.
	NextURL() (string, error)

	// BuildURL builds a url.URL struct for a resource name & method.
	//
	// resourceName should have the same value as the handler's ResourceName method.
	//
	// method is the HandleMethod constant that corresponds with the resource
	// method for which to build the URL. E.g. HandleCreate with build a URL that
	// corresponds with the CreateResource method.
	//
	// All URL variables should be named in the vars map.
	BuildURL(resourceName string, method HandleMethod, vars RouteVars) (*url.URL, error)

	// ResponseFormat returns the response format for the request, defaulting to "json" if
	// one is not specified using the "format" query parameter.
	ResponseFormat() string

	// ResourceID returns the resource id for the request, defaulting to an empty string if
	// there isn't one.
	ResourceID() string

	// Version returns the API version for the request, defaulting to an empty string if
	// one is not specified in the request path.
	Version() string

	// Status returns the current HTTP status code that will be returned for the request,
	// defaulting to 200 if one hasn't been set yet.
	Status() int

	// Error returns the current error for the request or nil if no errors have been set.
	Error() error

	// Result returns the result resource for the request or nil if no result has been set.
	Result() interface{}

	// Cursor returns the current result cursor for the request, defaulting to an empty
	// string if one hasn't been set.
	Cursor() string

	// Limit returns the maximum number of results that should be fetched.
	Limit() int

	// Messages returns all of the messages set by the request handler to be included in
	// the response.
	Messages() []string

	// AddMessage adds a message to the request messages to be included in the response.
	AddMessage(string)

	// Header returns the header key-value pairs for the request.
	Header() http.Header

	// Body returns a buffer containing the raw body of the request.
	Body() *bytes.Buffer

	// ResponseWriter Access to Response Writer Interface to allow for setting Response Header values
	ResponseWriter() http.ResponseWriter
	// contains filtered or unexported methods
}

RequestContext contains the context information for the current HTTP request. Context values are stored on the http.Request context.

func NewContext

func NewContext(req *http.Request, writer http.ResponseWriter) RequestContext

NewContext returns a RequestContext populated with parameters from the request path and query string.

func NewContextWithRouter

func NewContextWithRouter(req *http.Request, writer http.ResponseWriter, router *mux.Router) RequestContext

type RequestMiddleware

type RequestMiddleware func(http.Handler) http.Handler

RequestMiddleware is a function that returns a Handler wrapping the provided Handler. This allows injecting custom logic to operate on requests (e.g. performing authentication).

type Resource

type Resource interface{}

Resource represents a domain model.

type ResourceHandler

type ResourceHandler interface {
	// ResourceName is used to identify what resource a handler corresponds to and is
	// used in the endpoint URLs, i.e. /api/:version/resourceName. This should be
	// unique across all ResourceHandlers.
	ResourceName() string

	// CreateURI returns the URI for creating a resource.
	CreateURI() string

	// CreateDocumentation returns a string describing the handler's create endpoint.
	CreateDocumentation() string

	// ReadURI returns the URI for reading a specific resource.
	ReadURI() string

	// ReadDocumentation returns a string describing the handler's read endpoint.
	ReadDocumentation() string

	// ReadListURI returns the URI for reading a list of resources.
	ReadListURI() string

	// ReadListDocumentation returns a string describing the handler's read list endpoint.
	ReadListDocumentation() string

	// UpdateURI returns the URI for updating a specific resource.
	UpdateURI() string

	// UpdateDocumentation returns a string describing the handler's update endpoint.
	UpdateDocumentation() string

	// UpdateListURI returns the URI for updating a list of resources.
	UpdateListURI() string

	// UpdateListDocumentation returns a string describing the handler's update list
	// endpoint.
	UpdateListDocumentation() string

	// DeleteURI returns the URI for deleting a specific resource.
	DeleteURI() string

	// DeleteDocumentation returns a string describing the handler's delete endpoint.
	DeleteDocumentation() string

	// CreateResource is the logic that corresponds to creating a new resource at
	// POST /api/:version/resourceName. Typically, this would insert a record into a
	// database. It returns the newly created resource or an error if the create failed.
	CreateResource(RequestContext, Payload, string) (Resource, error)

	// ReadResourceList is the logic that corresponds to reading multiple resources,
	// perhaps with specified query parameters accessed through the RequestContext. This
	// is mapped to GET /api/:version/resourceName. Typically, this would make some sort
	// of database query to fetch the resources. It returns the slice of results, a
	// cursor (or empty) string, and error (or nil).
	ReadResourceList(RequestContext, int, string, string) ([]Resource, string, error)

	// ReadResource is the logic that corresponds to reading a single resource by its ID
	// at GET /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database query to load the resource. If the resource doesn't exist, nil should be
	// returned along with an appropriate error.
	ReadResource(RequestContext, string, string) (Resource, error)

	// UpdateResourceList is the logic that corresponds to updating a collection of
	// resources at PUT /api/:version/resourceName. Typically, this would make some
	// sort of database update call. It returns the updated resources or an error if
	// the update failed.
	UpdateResourceList(RequestContext, []Payload, string) ([]Resource, error)

	// UpdateResource is the logic that corresponds to updating an existing resource at
	// PUT /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database update call. It returns the updated resource or an error if the update
	// failed.
	UpdateResource(RequestContext, string, Payload, string) (Resource, error)

	// DeleteResource is the logic that corresponds to deleting an existing resource at
	// DELETE /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database delete call. It returns the deleted resource or an error if the delete
	// failed.
	DeleteResource(RequestContext, string, string) (Resource, error)

	// Authenticate is logic that is used to authenticate requests. The default behavior
	// of Authenticate, seen in BaseResourceHandler, always returns nil, meaning all
	// requests are authenticated. Returning an error means that the request is
	// unauthorized and any error message will be sent back with the response.
	Authenticate(*http.Request) error

	// ValidVersions returns the list of all versions accepted at this endpoint.
	// Invalid versions will result in a BadRequest error.
	// If the value is nil, any version will be accepted.
	ValidVersions() []string

	// Rules returns the resource rules to apply to incoming requests and outgoing
	// responses. The default behavior, seen in BaseResourceHandler, is to apply no
	// rules.
	Rules() Rules
}

ResourceHandler specifies the endpoint handlers for working with a resource. This consists of the business logic for performing CRUD operations.

type Response

type Response struct {
	Status   int            // HTTP status code.
	Reason   string         // Reason message for the status code.
	Messages []string       // Any server messages attached to the Response.
	Next     string         // A cursor to the next result set.
	Result   interface{}    // The decoded result of the REST request.
	Raw      *http.Response // The raw HTTP response.
}

Response is unmarshaled struct returned from an HTTP request.

type ResponseDecodeError

type ResponseDecodeError struct {
	StatusCode  int    // Response status code
	Status      string // Response status message
	Response    []byte // Payload of the response that could not be decoded
	DecodeError error  // Error that occurred while decoding the response
}

Wraps response decoding error in a helpful way

func (*ResponseDecodeError) Error

func (rde *ResponseDecodeError) Error() string

type ResponseSerializer

type ResponseSerializer interface {

	// Serialize marshals a response payload into a byte slice to be sent over the wire.
	Serialize(Payload) ([]byte, error)

	// ContentType returns the MIME type of the response.
	ContentType() string
}

ResponseSerializer is responsible for serializing REST responses and sending them back to the client.

type RestClient

type RestClient interface {
	// Get will perform an HTTP GET on the specified URL and return the response.
	Get(url string, header http.Header) (*Response, error)

	// Post will perform an HTTP POST on the specified URL and return the response.
	Post(url string, body interface{}, header http.Header) (*Response, error)

	// Put will perform an HTTP PUT on the specified URL and return the response.
	Put(url string, body interface{}, header http.Header) (*Response, error)

	// Delete will perform an HTTP DELETE on the specified URL and return the response.
	Delete(url string, header http.Header) (*Response, error)
}

RestClient performs HTTP methods including Get, Post, Put, and Delete

func NewRestClient

func NewRestClient(c HttpClient, middleware ...ClientMiddleware) RestClient

type RouteVars

type RouteVars map[string]string

RouteVars is a map of URL route variables to values.

vars = RouteVars{"category": "widgets", "resource_id": "42"}

Variables are defined in CreateURI and the other URI methods.

type Rule

type Rule struct {
	// Name of the resource field. This is the name as it appears in the struct
	// definition.
	Field string

	// Name of the input/output field. Use Name() to retrieve the field alias while
	// falling back to the field name if it's not specified.
	FieldAlias string

	// Type to coerce field value to. If the value cannot be coerced, an error will be
	// returned in the response. Defaults to Unspecified, which is the equivalent of
	// an interface{} value.
	Type Type

	// Indicates if the field must have a value. Defaults to false.
	Required bool

	// Versions is a list of the API versions this Rule applies to. If empty, it will
	// be applied to all versions.
	Versions []string

	// Indicates if the Rule should only be applied to requests.
	InputOnly bool

	// Indicates if the Rule should only be applied to responses.
	OutputOnly bool

	// Function which produces the field value to receive.
	InputHandler func(interface{}) interface{}

	// Function which produces the field value to send.
	OutputHandler func(interface{}) interface{}

	// Nested Rules to apply to field value.
	Rules Rules

	// Description used in documentation.
	DocString string

	// Example value used in documentation.
	DocExample interface{}
}

Rule provides schema validation and type coercion for request input and fine-grained control over response output. If a ResourceHandler provides input Rules which specify types, input fields will attempt to be coerced to those types. If coercion fails, an error will be returned in the response. If a ResourceHandler provides output Rules, only the fields corresponding to those Rules will be sent back. This prevents new fields from leaking into old API versions.

func (Rule) Applies

func (r Rule) Applies(version string) bool

Applies returns whether or not the Rule applies to the given version.

func (Rule) Name

func (r Rule) Name() string

Name returns the name of the input/output field alias. It defaults to the field name if the alias was not specified.

type Rules

type Rules interface {
	// Contents returns the contained Rules.
	Contents() []*Rule

	// ResourceType returns the reflect.Type these Rules correspond to.
	ResourceType() reflect.Type

	// Validate verifies that the Rules are valid, meaning they specify fields that exist
	// and correct types. If a Rule is invalid, an error is returned. If the Rules are
	// valid, nil is returned. This will recursively validate nested Rules.
	Validate() error

	// Filter will filter the Rules based on the specified Filter. Only Rules of the
	// specified Filter type will be returned.
	Filter(Filter) Rules

	// Size returns the number of contained Rules.
	Size() int

	// ForVersion returns the Rules which apply to the given version.
	ForVersion(string) Rules
}

Rules is a collection of Rules and a reflect.Type which they correspond to.

func NewRules

func NewRules(ptr interface{}, r ...*Rule) Rules

NewRules returns a set of Rules for use by a ResourceHandler. The first argument must be a resource pointer (and can be nil) used to associate the Rules with a resource type. If it isn't a pointer, this will panic.

type StdLogger

type StdLogger interface {
	Print(...interface{})
	Printf(string, ...interface{})
	Println(...interface{})

	Fatal(...interface{})
	Fatalf(string, ...interface{})
	Fatalln(...interface{})

	Panic(...interface{})
	Panicf(string, ...interface{})
	Panicln(...interface{})
}

An interface satisfied by log.Logger

type Type

type Type uint

Type is a data type to coerce a value to specified with a Rule.

const (
	Interface Type = iota
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Float32
	Float64
	String
	Bool
	Slice
	Map
	Duration
	Time
	Byte        = Uint8
	Unspecified = Interface
)

Type constants define the data types that Rules can specify for coercion.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL