ep

package module
v0.0.13 Latest Latest
Warning

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

Go to latest
Published: May 8, 2020 License: MIT Imports: 10 Imported by: 0

README

ep

A miniature Go(lang) framework for rapid development of http.Handlers while reducing code duplication and increasing readability. Designed to build both APIs and regular web applications more efficiently while keeping the flexibility that is expected in the Go ecosystem.

Features:

  • Works with any HTTP router that accepts the http.Handler interface
  • Supports any library for input validation, both system-wide or customized per endpoint
  • Provides customizable error handling for system errors and errors specific to your application
  • Automatically encodes and decodes HTTP payloads using content negotation
  • Uses language negotiation os your code can use best supported language for translations
  • Well tested, benchmarked and depends only on the standard library

Backlog

  • MUST get kitchen example back to work
  • MUST also add HTTP language negotiation
  • MUST output.Head and input.Check are now optional
  • MUST clean up the config and make config ergonomic
  • MUST allow exec to return a InvalidInput error
  • MUST allow default configuration to be configured
  • MUST test file upload usecase
  • MUST allow (url) query decoding when request body is JSON (first decoder to implement an interface is used?)
  • MUST have an form decoding that just takes an interface to do the actual decoding
  • MUST benchmark worst case sniffing, negotiation and base overhead
  • MUST run with race checker to check for race conditions
  • MUST allow outputs to overwrite the template to use
  • MUST be able to cache output templates
  • MUST be ergonomic to have translated templates as a response, or other (error) customizations
  • MUST fully test coding package
  • MUST find an alternative for comparing error interface values in Render: not needed, users can just retur nil
  • MUST have a better way to debug unexpected error responses for development: add client and server error logging
  • MUST handle panics in the handle, with the server error message rendering, should also be easy to debug
  • MUST re-think usecase of rest endpoint returning error
  • MUST don't write body if response is 204 or other status without a body
  • MUST allow html template to accept any kind of template (interface), rename to template encoding
  • MUST not server 500 status code if skipEncode is provided as an error to render
  • MUST set default error template to "error.html" it is corresponds to an actual file in the most common case
  • MUST have package level doc summary for coding package
  • SHOULD have a clearer error when here is no html template defined for "error"
  • SHOULD allow configuring defaults for endpoint config
  • SHOULD make the Config method more ergonomic to use
  • SHOULD come with build-in logging support to debug client and server errors
  • SHOULD remove progress keeping from reader
  • SHOULD be able to return all kinds of app errors with status code from exec
  • SHOULD also make it more ergonomic to just render a 204, 404, Conflict and other common exec status responses for REST endpoints
  • SHOULD make it ergonomic to render output with common 2xx status codes: 201, 204
  • SHOULD make it ergonomic to redirect the client
  • SHOULD make AppError fields public
  • SHOULD rename "Check" on input to "Validate", way more obvious and less suprising
  • SHOULD add more logging methods to the logger to track
  • SHOULD SkipEncode should also work when returned directly to the render
  • SHOULD in general, make it easier to return some response with just a status code and a simple body (no encoding)
  • COULD not get nil pointer if status created is embedded on a nil output struct. Instead, embedding should trigger behaviour differently
  • COULD use the configuration pattern as described here: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
  • COULD turn most of the coding tests into table tests
  • COULD provide tooling to make endpoints extremely easy to test
  • COULD provide tooling to fuzz endpoint
  • COULD add Conf constructors for different types of endpoints: Rest, Form
  • COULD make config method on endpoint optional
  • COULD move per endpoint config to where Handler is called instead
  • COULD come with a nice error page for development
  • COULD rename 'epcoding' to just 'coding'
  • COULD rename coding to something else entirely, cofusing with HTTP encoding header name
  • COULD create http request interface for easier testing
  • COULD remove reqProgress counter
  • COULD allow input.Read to return special error that prevents decoding
  • COULD allow output.Head to return special error that prevents encoding
  • COULD better test language negotiation
  • COULD support response buffering for errors that occur halway writing the response
  • COULD allow JSON encoder configuration, i.e: indentation
  • COULD be more flexible with what content get's accepted for decoding: (i.e application/vnd.api+json should match json)
  • COULD allow configuration what content-type will be written for a encoder: i.e: application/vnd.api+json
  • COULD also handle panics in the negotiation code
  • COULD assert status codes send to Error, Errorf to be in range of 400-600
  • COULD support something like this: https://github.com/mozillazg/go-httpheader on output structs
  • COULD encode response status also from output struct tags: maybe use AWS SDK approach of tagging with 'location:"header/uri/body"'
  • WONT do content-encoding negotiation, complex: https://github.com/nytimes/gziphandler, deserves dedicated package
  • WONT add a H/HF method for endpoints that are just the handle/exec func
  • WONT return an error from handle as well, since that might be a common usecase. We want to motivate to move into exec function

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// SkipEncode can be retured by the output head to prevent any further
	// decoding
	SkipEncode = errors.New("skip encode")
)

Functions

func Decoding

func Decoding(ctx context.Context) (dec epcoding.Decoding)

Decoding will return the request decoding that was determined based on request headers and MIME sniffing. This will only be empty if the server didn't configure any decodings.

func Encoding

func Encoding(ctx context.Context) (enc epcoding.Encoding)

Encoding will return the response encoding as negotiated with the client. This will only be empty if the server didn't specify any supported encodings

func Language

func Language(ctx context.Context) (s string)

Language will return the language as negotiated with the client. This will only be empty if the server didn't provide any languages

func Negotiate

func Negotiate(cfg ConfReader, req *http.Request) *http.Request

Negotiate will look at the requests' headers and set context values for encoding, decoding and language.

Types

type AppError added in v0.0.7

type AppError struct {
	Code int
	Err  error
}

AppError is an application specific error that can be returned by application code to trigger the rendering of error output with a specific status code.

func Error added in v0.0.7

func Error(code int, err ...error) *AppError

Error will create an AppError with at most 1 wrapped error. If the no error is provided the resulting output will show the default http status message for the provided code.

func Errorf added in v0.0.7

func Errorf(code int, f string, args ...interface{}) *AppError

Errorf will create a new AppError that wraps a new formatted error message. The f string may contain %w to wrap an error.

func (AppError) Error added in v0.0.7

func (e AppError) Error() string

Error implements the error interface

func (AppError) Unwrap added in v0.0.7

func (e AppError) Unwrap() error

Unwrap allows this error to be unwrapped

type CheckerInput

type CheckerInput interface {
	Input
	Validate() (err error)
}

CheckerInput can be implemented by Inputs to allow them to validate themselves

type Conf

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

Conf builds endpoint configuration

func New

func New() *Conf

New inits an empty configuration

func (Conf) AppErrFactory added in v0.0.7

func (r Conf) AppErrFactory() func(err *AppError) Output

AppErrFactory returns the current invalid input error factory

func (Conf) ClientErrFactory

func (r Conf) ClientErrFactory() func(err error) Output

ClientErrFactory returns the current client error factory

func (Conf) Copy

func (c Conf) Copy() (cc *Conf)

Copy will duplicate the configuration

func (Conf) Decodings

func (c Conf) Decodings() []epcoding.Decoding

Decodings returns the configured input decodings

func (Conf) Encodings

func (c Conf) Encodings() []epcoding.Encoding

Encodings returns the configured output encodings

func (Conf) Handler

func (c Conf) Handler(ep Endpoint) *Handler

Handler will copy the configuration and make the endpoint as a handler

func (Conf) HandlerFunc

func (c Conf) HandlerFunc(epf EndpointFunc) *Handler

HandlerFunc will copy the configuration the func as a handler

func (Conf) Languages

func (c Conf) Languages() []string

Languages return the supported languages for this endpoint

func (Conf) Logger added in v0.0.6

func (c Conf) Logger() Logger

Logger returns the configured logger

func (Conf) QueryDecoder

func (c Conf) QueryDecoder() epcoding.URLValuesDecoder

QueryDecoder configures the query to be decoded into the input struct

func (Conf) ServerErrFactory

func (r Conf) ServerErrFactory() func(err error) Output

ServerErrFactory returns the configured factory for server errors

func (*Conf) SetAppErrFactory added in v0.0.7

func (r *Conf) SetAppErrFactory(f func(err *AppError) Output) *Conf

SetAppErrFactory configures how invalid input errors are created

func (*Conf) SetClientErrFactory

func (r *Conf) SetClientErrFactory(f func(err error) Output) *Conf

SetClientErrFactory configures how client error outputs are created

func (*Conf) SetDecodings

func (c *Conf) SetDecodings(decs ...epcoding.Decoding) *Conf

func (*Conf) SetEncodings

func (c *Conf) SetEncodings(encs ...epcoding.Encoding) *Conf

func (*Conf) SetLanguages

func (c *Conf) SetLanguages(langs ...string) *Conf

func (*Conf) SetLogger added in v0.0.6

func (c *Conf) SetLogger(l Logger) *Conf

func (*Conf) SetQueryDecoder

func (c *Conf) SetQueryDecoder(d epcoding.URLValuesDecoder) *Conf

func (*Conf) SetServerErrFactory

func (r *Conf) SetServerErrFactory(f func(err error) Output) *Conf

SetServerErrFactory configures a factory for the creation of server error outputs

func (*Conf) SetValidator

func (c *Conf) SetValidator(v Validator) *Conf

func (Conf) Validator

func (c Conf) Validator() Validator

Validator returns the configured input validator

func (*Conf) WithDecoding

func (c *Conf) WithDecoding(decs ...epcoding.Decoding) *Conf

func (*Conf) WithEncoding

func (c *Conf) WithEncoding(encs ...epcoding.Encoding) *Conf

func (*Conf) WithLanguage

func (c *Conf) WithLanguage(langs ...string) *Conf

type ConfReader

type ConfReader interface {
	Encodings() []epcoding.Encoding
	Decodings() []epcoding.Decoding
	Languages() []string
	Validator() Validator
	Logger() Logger
	QueryDecoder() epcoding.URLValuesDecoder
	ServerErrFactory() func(err error) Output
	ClientErrFactory() func(err error) Output
	AppErrFactory() func(err *AppError) Output
}

type Endpoint

type Endpoint interface {
	Handle(*Response, *http.Request)
}

Endpoint can be implemented to descibe an HTTP endpoint

type EndpointFunc

type EndpointFunc func(*Response, *http.Request)

EndpointFunc implements endpoint by providng just the Handle func

func (EndpointFunc) Handle

func (f EndpointFunc) Handle(res *Response, req *http.Request)

type Handler

type Handler struct {
	*Conf
	// contains filtered or unexported fields
}

Handler handles http for certain endpoint

func (Handler) ServeHTTP

func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP allows the endpoint to serve HTTP

type HeaderOutput

type HeaderOutput interface {
	Output
	Head(http.ResponseWriter, *http.Request) error
}

HeaderOutput can be optionally implementedd by outputs to customize the response headers

type Input

type Input interface{}

Input describes what is read from a request as input to the endpoint.

type Logger added in v0.0.6

type Logger interface {

	// LogServerErrRender is called when the response will render a server error
	LogServerErrRender(err error)

	// LogClientErrRender is called when the response will render a client error
	LogClientErrRender(err error)

	// LogAppErrRender is called when the response will be a rendered app errror
	LogAppErrRender(err *AppError)
}

Logger interface may be implemented to allow the endpoints to provide feedback in what ever way is preferred

type NopLogger added in v0.0.6

type NopLogger struct{}

NopLogger is can be provided to disable logging

func (NopLogger) LogAppErrRender added in v0.0.7

func (l NopLogger) LogAppErrRender(err *AppError)

func (NopLogger) LogClientErrRender added in v0.0.6

func (l NopLogger) LogClientErrRender(err error)

func (NopLogger) LogServerErrRender added in v0.0.6

func (l NopLogger) LogServerErrRender(err error)

type Output

type Output interface{}

An Output represents one item that results from the endpoint is will be send as the response back to the client.

type Reader

type Reader struct {
	io.Closer
	*bufio.Reader
}

Reader is a buffered reader that keeps track of the number of bytes that have been read.

func NewReader

func NewReader(r io.ReadCloser) *Reader

NewReader turns an unbuffered reader into a buffered read that keeps track of reading progress. The buffer size is 512 bytes to allow MIME sniffing of the readers content

func (*Reader) Sniff

func (r *Reader) Sniff() (ct string)

Sniff will use peek into the reader and tries to determine the content type

type ReaderInput

type ReaderInput interface {
	Input
	Read(r *http.Request) error
}

ReaderInput is an input that may optionally be implemented by inputs to indicate that it has custom logic for reading the request.

type Response

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

Response is an http.ResponseWriter implementation that comes with a host of untility method for common tasks in http handling.

func NewResponse

func NewResponse(
	wr http.ResponseWriter,
	req *http.Request,
	cfg ConfReader,
) (res *Response)

NewResponse initializes a new Response

func (*Response) Bind

func (r *Response) Bind(in Input) (ok bool)

Bind will use the negotiated decoder to populate the input.

func (*Response) Error

func (r *Response) Error() error

Error will return any server, client or validation error that was encountered while formulating the response.

func (*Response) Header

func (r *Response) Header() http.Header

Header implements the http.ResponseWriter's "Header" method

func (*Response) RecoverRender added in v0.0.6

func (r *Response) RecoverRender()

RecoverRender allows the response to recover from a panic in the stack and render an internal server error.

func (*Response) Render

func (r *Response) Render(out Output, err error)

Render will assert the provided error and earlier errors and provide appropriate feedback in the response. If 'err' is not the same error as returned by Validate() it will be handled as a server error.

func (*Response) Validate

func (r *Response) Validate(in Input) (verr error)

Validate will validate the input and return any error. It will first use any struct validator first before using the input's check method.

func (*Response) Write

func (r *Response) Write(b []byte) (int, error)

Write implements the http.ResponseWriter's "Write" method

func (*Response) WriteHeader

func (r *Response) WriteHeader(statusCode int)

WriteHeader implements the http.ResponseWriter's "WriteHeader" method

type StatusCreated added in v0.0.7

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

StatusCreated can be embedded to automatically set the setatus code to 201 when it is rendered as output. If SetLocation is called the Location header will also be set.

func (StatusCreated) Head added in v0.0.7

func (*StatusCreated) SetLocation added in v0.0.7

func (s *StatusCreated) SetLocation(l string)

type StatusNoContent added in v0.0.7

type StatusNoContent struct{}

StatusNoContent can be embedded to automatically set the response status code to 204 and prevent any body from being returned.

func (StatusNoContent) Head added in v0.0.7

type StatusRedirect added in v0.0.7

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

StatusRedirect can be embedded to render a redirect using http.Redirect. It will only do so if SetRedirect is called with a non-empty string.

func (StatusRedirect) Head added in v0.0.7

func (s StatusRedirect) Head(w http.ResponseWriter, r *http.Request) (err error)

func (*StatusRedirect) SetRedirect added in v0.0.7

func (s *StatusRedirect) SetRedirect(l string, code ...int)

SetRedirect will cause the response to be a redirect. By default it will be a '303 See Other' redirect unless the code is used set it to another 3xx status code.

type StdLogger added in v0.0.6

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

StdLogger creates a logger using the standard library logging package

func NewStdLogger added in v0.0.6

func NewStdLogger(logs *log.Logger) *StdLogger

NewStdLogger inits a new standard library logger. If logs is nil it will use the global logs.* methods for printing

func (StdLogger) LogAppErrRender added in v0.0.7

func (l StdLogger) LogAppErrRender(err *AppError)

func (StdLogger) LogClientErrRender added in v0.0.6

func (l StdLogger) LogClientErrRender(err error)

func (StdLogger) LogServerErrRender added in v0.0.6

func (l StdLogger) LogServerErrRender(err error)

type Validator

type Validator interface {
	Validate(v interface{}) error
}

Validator may be implemented and configured to allow automatic validation of all endpoint inputs.

Directories

Path Synopsis
Package header provides functions for parsing HTTP headers.
Package header provides functions for parsing HTTP headers.

Jump to

Keyboard shortcuts

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