common

package
v0.321.0 Latest Latest
Warning

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

Go to latest
Published: Mar 14, 2024 License: Apache-2.0 Imports: 29 Imported by: 5

README

Error Responses

Overview

By default, error responses will all look something along the lines of:

{
    "status":
      {
          "code": "321",
          "description": "Error Description"
      }
}

There are a number of different ways to modify how the errors are returned listed below (the most common being MapError).

Types and Interfaces

CustomError

type CustomError map[string]string

Will map to an HTTPError based on the keys in the map:

  • http_status - will run strconv.Atoi on the value and use it as the HTTPCode member
  • http_code - maps to the Code member
  • http_message - maps to the Description member

Custom error types can be automatically generated by annotating error types with the error tag, for example, this sysl:

    !type UnauthorizedError [~error]:
        http_status <: string [value = "401"]
        http_code <: string [value = "1003"]
        http_message <: string [value = "Unauthorized"]

Will generate this type:

var UnauthorizedError common.CustomError = map[string]string{
	"name":         "UnauthorizedError",
	"http_code":    "1003",
	"http_message": "Unauthorized",
	"http_status":  "401",
}

ErrorWriter

// If the error returned is an ErrorWriter the error handling will call the writeError method before any of the
// regular error handling (no mapping).
//
// If the call returns true it means it wrote the error and will not do any more handling.
// If it returns false it will go through the normal error writing path (via both the MapError and WriteError callbacks).
type ErrorWriter interface {
	WriteError(ctx context.Context, w http.ResponseWriter) bool
}

If the error returned from a handler is an ErrorWriter then it will call the WriteError method directly instead of the normal error mapping path. Note this means that if true is returned from the call, then none of the other types or callbacks will be used or called for this response.

HTTPError

type HTTPError struct {
	HTTPCode    int    `json:"-"`
	Code        string `json:"code,omitempty"`
	Description string `json:"description,omitempty"`
}

The normal error response path will map all errors into an HTTPError which will then be converted to json (see Overview for an example) and returned under the status attribute with the status code taken from the HTTPCode field.

WrappedError

*Note this type is deprecated, prefer using a different method, for example you could call AddField directly on the HTTPError or use an ErrorWriter or WriteError to get full access to the response writing.

func WrappedError(err error, fields ...KV) error {
	return wrappedError{
		e:      err,
		fields: fields,
	}
}

type KV struct {
    K string
    V interface{}
}

You can return the value of WrappedError to add additional fields to the response json message. K will be used as attribute and json.Marshal will be called on V to produce the value.

Callbacks

MapError

// MapError maps an error to an HTTPError in instances where custom error mapping is required.
// Return nil to perform default error mapping; defined as:
// 1. CustomError.HTTPError if the original error is a CustomError, otherwise
// 2. common.MapError
MapError func(ctx context.Context, err error) *common.HTTPError

All errors (both those produced by sysl-go library and by the handlers) will go through this mapping to map them to an HTTPError before being written to the http.ResponseWriter.

This is the most common callback used for manipulating the response values before they sent back.

For example creating a new server would look something like:

func newAppServer(ctx context.Context) (core.StoppableServer, error) {
	return example.NewServer(ctx, func(ctx context.Context, cfg AppConfig) (*example.ServiceInterface, *core.Hooks, error) {
		return &example.ServiceInterface{
				//...
			}, &core.Hooks{
				MapError: func(ctx context.Context, err error) *common.HTTPError {
					return &common.HTTPError{
						// fill details here based on err
					}
				},
			},
			nil
	})
}

WriteError

// WriteError can be used to write the error to the writer in whatever way you want.
// If not supplied it will use httpError.WriteError as the default.
WriteError func(ctx context.Context, w http.ResponseWriter, httpError *common.HTTPError)

If you need to modify the format that the responses are written, you can supply a WriteError callback that will be called instead of httpError.WriteError.

For example creating a new server would look something like:

func newAppServer(ctx context.Context) (core.StoppableServer, error) {
	return example.NewServer(ctx, func(ctx context.Context, cfg AppConfig) (*example.ServiceInterface, *core.Hooks, error) {
		return &example.ServiceInterface{
				//...
			}, &core.Hooks{
				WriteError: func(ctx context.Context, w http.ResponseWriter, httpError *common.HTTPError) {
					// These next few lines are just an example of what could be done
					w.Header().Set("Content-Type", "application/json;charset=UTF-8")
					w.WriteHeader(httpError.HTTPCode)
					w.Write([]byte{
						// fill details here based on httpError
					})
				},
			},
			nil
	})
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrHandlerTimeout = errors.New("http: Handler timeout")

ErrHandlerTimeout is returned on ResponseWriter Write calls in handlers which have timed out.

Functions

func AddTraceIDToContext

func AddTraceIDToContext(ctx context.Context, id uuid.UUID, wasProvided bool) context.Context

func AppendToResponseHeader added in v0.259.0

func AppendToResponseHeader(ctx context.Context, key string, value string) error

AppendToResponseHeader will add custom fields to the response header.

func CheckContextTimeout

func CheckContextTimeout(ctx context.Context, message string, cause error) error

func CoreRequestContextMiddleware

func CoreRequestContextMiddleware(next http.Handler) http.Handler

func CreateDownstreamError

func CreateDownstreamError(ctx context.Context, kind Kind, response *http.Response, body []byte, cause error) error

func CreateError

func CreateError(ctx context.Context, kind Kind, message string, cause error) error

func FindConfigFilename

func FindConfigFilename(cfgDir, prefix string) string

func GetLogEntryFromContext deprecated

func GetLogEntryFromContext(ctx context.Context) *logrus.Entry

Deprecated: Use log.GetLogger.

func GetLoggerFromContext deprecated

func GetLoggerFromContext(ctx context.Context) *logrus.Logger

Deprecated: Use log.GetLogger.

func GetTraceIDFromContext

func GetTraceIDFromContext(ctx context.Context) uuid.UUID

func HandleError

func HandleError(
	ctx context.Context,
	w http.ResponseWriter,
	kind Kind,
	message string,
	cause error,
	httpErrorMapper func(context.Context, error) *HTTPError,
	httpErrorWriter func(ctx context.Context, w http.ResponseWriter, httpError *HTTPError),
)

func LoadAndValidateFromYAMLFileName

func LoadAndValidateFromYAMLFileName(filename string, out validator.Validator) error

func LoggerToContext deprecated

func LoggerToContext(ctx context.Context, logger *logrus.Logger, entry *logrus.Entry) context.Context

Deprecated: Use log.GetLogger.

func NewBool

func NewBool(b bool) *bool

func NewHTTPTestServer added in v0.175.0

func NewHTTPTestServer(handler http.Handler) *httptest.Server

NewHTTPTestServer returns a new httptest.Server with the given handler suitable for use within unit tests. The returned server comes equipped with the following: 1. log.Logger.

func NewInvalidHeaderError added in v0.258.0

func NewInvalidHeaderError(param string, cause error) error

func NewLoggingRoundTripper

func NewLoggingRoundTripper(name string, base http.RoundTripper) http.RoundTripper

func NewString

func NewString(s string) *string

func NewUnstartedHTTPTestServer added in v0.175.0

func NewUnstartedHTTPTestServer(handler http.Handler) *httptest.Server

NewHTTPTestServer returns an unstarted httptest.Server with the given handler suitable for use within unit tests. The returned server comes equipped with the following: 1. log.Logger.

func NewZeroHeaderLengthError added in v0.211.0

func NewZeroHeaderLengthError(param string) error

func ProvisionRestResult added in v0.69.0

func ProvisionRestResult(ctx context.Context) context.Context

ProvisionRestResult provisions within the context the ability to retrieve the result of a rest request.

func RequestHeaderFromContext

func RequestHeaderFromContext(ctx context.Context) http.Header

RequestHeaderFromContext retrieves the request header from the context.

func RequestHeaderToContext

func RequestHeaderToContext(ctx context.Context, header http.Header) context.Context

RequestHeaderToContext creates a new context containing the request header.

func RespHeaderAndStatusFromContext

func RespHeaderAndStatusFromContext(ctx context.Context) (header http.Header, status int)

RespHeaderAndStatusFromContext retrieves response header and status from the context.

func RespHeaderAndStatusToContext

func RespHeaderAndStatusToContext(ctx context.Context, header http.Header, status int) context.Context

RespHeaderAndStatusToContext creates a new context containing the response header and status.

func TemporalClientFrom added in v0.297.0

func TemporalClientFrom(ctx context.Context, taskqueue string) (client.Client, error)

func Timeout

func Timeout(timeout time.Duration, timeoutHandler http.Handler) func(next http.Handler) http.Handler

Timeout is a middleware that cancels ctx after a given timeout or call a special handler on timeout.

func TimeoutHandler

func TimeoutHandler(h http.Handler, dt time.Duration, timeout http.Handler) http.Handler

TimeoutHandler returns a Handler that runs h with the given time limit.

The new Handler calls h.ServeHTTP to handle each request, but if a call runs for longer than its time limit, the handler calls the error handler which can then return a HTTP result however is deems correct. After such a timeout, writes by h to its ResponseWriter will return ErrHandlerTimeout.

TimeoutHandler supports the Pusher interface but does not support the Hijacker or Flusher interfaces.

func TraceabilityMiddleware

func TraceabilityMiddleware(next http.Handler) http.Handler

Injects a traceId UUID into the request context.

func TryGetTraceIDFromContext

func TryGetTraceIDFromContext(ctx context.Context) (uuid.UUID, bool)

func UpdateResponseStatus

func UpdateResponseStatus(ctx context.Context, status int) error

func WithTemporalClientMap added in v0.297.0

func WithTemporalClientMap(ctx context.Context, m map[string]client.Client) context.Context

func WrappedError added in v0.31.0

func WrappedError(err error, fields ...KV) error

Types

type AsyncFunc added in v0.266.0

type AsyncFunc func(ctx context.Context) (interface{}, error)

type AsyncResult added in v0.266.0

type AsyncResult struct {
	Obj interface{}
	Err error
}

type Callback

type Callback struct {
	DownstreamTimeout time.Duration
	RouterBasePath    string
	UpstreamConfig    validator.Validator
	MapErrorFunc      func(ctx context.Context, err error) *HTTPError                        // MapErrorFunc may be left nil to use default behaviour.
	WriteErrorFunc    func(ctx context.Context, w http.ResponseWriter, httpError *HTTPError) // WriteError may be left nil to use default behaviour.
	AddMiddlewareFunc func(ctx context.Context, r chi.Router)                                // AddMiddlewareFunc may be left nil to use default behaviour.
}

func DefaultCallback

func DefaultCallback() Callback

func NewCallback added in v0.175.0

func NewCallback(
	config *config.GenCodeConfig,
	downstreamTimeOut time.Duration,
	mapError func(ctx context.Context, err error) *HTTPError,
) Callback

NewCallback is deprecated, prefer NewCallbackV2.

func NewCallbackV2 added in v0.175.0

func NewCallbackV2(
	config *config.GenCodeConfig,
	downstreamTimeOut time.Duration,
	mapError func(ctx context.Context, err error) *HTTPError,
	addMiddleware func(ctx context.Context, r chi.Router),
) Callback

NewCallbackV2 is deprecated, prefer NewCallbackV3.

func NewCallbackV3 added in v0.248.0

func NewCallbackV3(
	config *config.GenCodeConfig,
	downstreamTimeOut time.Duration,
	mapError func(ctx context.Context, err error) *HTTPError,
	writeError func(ctx context.Context, w http.ResponseWriter, httpError *HTTPError),
	addMiddleware func(ctx context.Context, r chi.Router),
) Callback

func (Callback) AddMiddleware

func (g Callback) AddMiddleware(ctx context.Context, r chi.Router)

func (Callback) BasePath

func (g Callback) BasePath() string

func (Callback) Config

func (g Callback) Config() interface{}

func (Callback) DownstreamTimeoutContext

func (g Callback) DownstreamTimeoutContext(ctx context.Context) (context.Context, context.CancelFunc)

func (Callback) HandleError

func (g Callback) HandleError(ctx context.Context, w http.ResponseWriter, kind Kind, message string, cause error)

func (Callback) MapError added in v0.58.0

func (g Callback) MapError(ctx context.Context, err error) *HTTPError

MapError maps an error to an HTTPError in instances where custom error mapping is required. Return nil to perform default error mapping; defined as: 1. CustomError.HTTPError if the original error is a CustomError, otherwise 2. common.MapError.

func (Callback) WriteError added in v0.248.0

func (g Callback) WriteError(ctx context.Context, w http.ResponseWriter, httpError *HTTPError)

type Config

type Config struct{}

func (Config) Validate

func (c Config) Validate() error

type CustomError added in v0.5.0

type CustomError map[string]string

func (CustomError) Error added in v0.5.0

func (e CustomError) Error() string

func (CustomError) HTTPError added in v0.5.0

func (e CustomError) HTTPError(ctx context.Context) *HTTPError

type DownstreamError

type DownstreamError struct {
	Kind     Kind
	Response *http.Response
	Body     []byte
	Cause    error
}

func (*DownstreamError) Error

func (e *DownstreamError) Error() string

func (*DownstreamError) ErrorKind

func (e *DownstreamError) ErrorKind() Kind

func (*DownstreamError) Unwrap

func (e *DownstreamError) Unwrap() error

type ErrorKinder

type ErrorKinder interface {
	ErrorKind() Kind
}

type ErrorWriter added in v0.248.0

type ErrorWriter interface {
	WriteError(ctx context.Context, w http.ResponseWriter) bool
}

If the error returned is an ErrorWriter the error handling will call the writeError method before any of the regular error handling (no mapping).

If the call returns true it means it wrote the error and will not do any more handling. If it returns false it will go through the normal error writing path (via both the MapError and WriteError callbacks).

type Future added in v0.266.0

type Future interface {
	// Calling future.Get() will block until the Async function either returns a value or panics or the context is done
	Get() (interface{}, error)

	// GetChan will return a channel that the AsyncResult can be read from.
	// Panic and context handling is already handled by Async so there is no need to use a select with a context to cancel
	// the blocking call
	GetChan() <-chan AsyncResult
}

A Future is an object that can retrieve a value from an Async call.

func Async added in v0.266.0

func Async(ctx context.Context, fn AsyncFunc) Future

Async executes the passed in fn in a new go routine and returns a Future which can be called to retrieve the result from the go routine. This will capture any panic() in the Future as well as manages channels to pass the returned error and result back to the caller.

type HTTPError

type HTTPError struct {
	HTTPCode    int    `json:"-"`
	Code        string `json:"code,omitempty"`
	Description string `json:"description,omitempty"`
	// contains filtered or unexported fields
}

func MapError added in v0.5.0

func MapError(ctx context.Context, err error) HTTPError

func (*HTTPError) AddField added in v0.31.0

func (httpError *HTTPError) AddField(key string, val interface{})

func (*HTTPError) GetField added in v0.248.0

func (httpError *HTTPError) GetField(key string) interface{}

func (*HTTPError) WriteError

func (httpError *HTTPError) WriteError(ctx context.Context, w http.ResponseWriter)

type InvalidHeaderError added in v0.258.0

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

func (*InvalidHeaderError) CausedByParam added in v0.258.0

func (e *InvalidHeaderError) CausedByParam(param string) bool

func (*InvalidHeaderError) Error added in v0.258.0

func (e *InvalidHeaderError) Error() string

func (*InvalidHeaderError) GetCause added in v0.258.0

func (e *InvalidHeaderError) GetCause() error

This may be nil (for example if the cause was a regular expression mismatch) or may be InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors (there may be more than one).

type KV added in v0.31.0

type KV struct {
	K string
	V interface{}
}

type Kind

type Kind int
const (
	UnknownError Kind = iota
	BadRequestError
	InternalError
	UnauthorizedError
	DownstreamUnavailableError
	DownstreamTimeoutError
	DownstreamUnauthorizedError       // 401 from downstream
	DownstreamUnexpectedResponseError // unexpected response from downstream
	DownstreamResponseError           // application-leve error response from downstream
)

func (Kind) String

func (k Kind) String() string

type MockRoundTripper

type MockRoundTripper struct {
	mock.Mock
}

func (*MockRoundTripper) RoundTrip

func (m *MockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

type RegexWithFallBack added in v0.258.0

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

func RegexWithFallbackMustCompile added in v0.258.0

func RegexWithFallbackMustCompile(str string) *RegexWithFallBack

func (*RegexWithFallBack) MatchString added in v0.258.0

func (r *RegexWithFallBack) MatchString(s string) bool

type RestResult added in v0.69.0

type RestResult struct {
	StatusCode int
	Headers    map[string][]string
	Body       []byte
}

func GetRestResult added in v0.69.0

func GetRestResult(ctx context.Context) *RestResult

GetRestResult gets the result of the most recent rest request. The context must be provisioned prior to the request taking place with a call to ProvisionRestResult.

type RestResultContextKey added in v0.69.0

type RestResultContextKey struct{}

type ServerError

type ServerError struct {
	Kind    Kind
	Message string
	Cause   error
}

func (*ServerError) Error

func (e *ServerError) Error() string

func (*ServerError) ErrorKind

func (e *ServerError) ErrorKind() Kind

func (*ServerError) Unwrap

func (e *ServerError) Unwrap() error

type ZeroHeaderLengthError added in v0.211.0

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

func (*ZeroHeaderLengthError) CausedByParam added in v0.211.0

func (e *ZeroHeaderLengthError) CausedByParam(param string) bool

func (*ZeroHeaderLengthError) Error added in v0.211.0

func (e *ZeroHeaderLengthError) Error() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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