Categorygithub.com/lab259/errors
modulepackage
1.0.0
Repository: https://github.com/lab259/errors.git
Documentation: pkg.go.dev

# README

CircleCI

[WIP] errors

THIS DOCUMENT IS A WORK IN PROGRESS

Errors have always been a problem in rthe development of our servers in Go. This package aims to address the main problems, centralizing a solution.

Scenario

Let's start by creating our scenario: The user Astrobervaldo already signed in system S. Astrobervaldo tries to create a new TODO item which fails because of an invalid JSON was sent as input.

API

In APIs, the probable endpoints would be implemented in a POST to /v1/todo. On this endpoint, one of the first things that would happen is to parse the request body in order to extract what Astrobervaldo sent. As we can image, it probably would use a json.Unmarshal and it would return an error.

What should we say to the user? What structure will be used? Is it centralized?

GraphQL

TODO

Solution

The idea is to wrap errors with simple structures that will aggregate more and more little pieces of information about the error.

Consider the following code:

err := json.Unmarshal(data, &todoItemInput)
if err != nil {
    return errors.WrapHttp(err, 400) // BadRequest
}

The code above shows an error being wrapped as an HttpError. Upwards, it can be checked and the status code, inside of it, can be applied to a possible HTTP response structure.

To improve the usage, a Wrap function was created:

err := json.Unmarshal(data, &todoItemInput)
if err != nil {
    return errors.Wrap(err, Http(400)) // BadRequest
}

This example has the same effect of the example shown previously. The difference is that Wrap is a variadic function. Yes, it can receive multiple arguments and make multiple wraps:

err := json.Unmarshal(data, &todoItemInput)
if err != nil {
    return errors.Wrap(err, Http(400), Code("invalid-json")) // BadRequest
}

In this example, err was wrapped twice. With a HttpError and a ReportableError. In other words, the error returned is a ReportableError with a HttpError as Reason, this HttpError has the original err as Reason.

How do the pieces of info get together?

For that, an interface type ErrorResponse interface was implemented.

An ErrorResponse aggregates all extra information about an error. The idea is that an ErrorResponse will be serialized and sent whom is using the system.

How aggregation occurs?

Some errors, those that need to add information to the ErrorResponse, implement another interface ErrorResponseAggregator. This interface has a AppendData(ErrorResponse) which get called for each Reason recursively:

err := json.Unmarshal(data, &todoItemInput)
if err != nil {
    return errors.WrapHttp(err, 400) // BadRequest
}

Here, the returned error would have its AppendData method called, and only this one because its Reason is not an ErrorResponseAggregator (it is the original unmarshaling error).

err := json.Unmarshal(data, &todoItemInput)
if err != nil {
    return errors.Wrap(err, Http(400), Code("invalid-json")) // BadRequest
}

In this case, a ReportableError would have its AppendData called, a HttpError would have its AppendData called afterwards, and then the original the unmarshaling error would be reached and nothing more would happen.

Error Types

HttpError

This error adds the StatusCode information to the error.

Wrap: Http(statusCode int): It returns a default HttpError implementation which is also a ErrorResponseAggregator.

ModuleError

This error adds the Module information to the error. The module specifies what area of the system the error is emerging from.

Wrap: Module(module string): It returns a default ModuleError implementation which is also a ErrorResponseAggregator.

ErrorWithCode

This error means something special, it marks the error can be reported to the final client. Otherwise, the idea is that an unknown error blows to the actor which is consuming the system.

Wrap: Code(module string): It returns a default CodeError implementation which is also a ErrorResponseAggregator.

ValidationError

A special case error for dealing with validation errors from the go-playground/validator package.

Wrap: Validation(): It returns a default ValidationError implementation which is also a ErrorResponseAggregator.

# Packages

No description provided by the author

# Functions

AggregateToResponse add the error information pieces to the errResponse.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
WrapCode creates a new instance of a Reportable error with the given code.
WrapHttp wraps a reason with an HttpError.
WrapCode creates a new instance of a Reportable error with the given code.
WrapModule wraps an original error with the module information, returning a new instance with the reason set.
No description provided by the author

# Variables

New is an alias for the default `errors.New`.

# Structs

ValidationError implements wrapping validation errors into a reportable.

# Interfaces

ErrorResponse is an abstraction of an error response that might, or not, be sent to the customer.
ErrorResponseAggregator is an error that can modify an `ErrorResponse`.
ErrorWithCode implements an error with a code related to it.
ErrorWithMessage implements an error with a code related to it.
ErrorWithReason is an error that has an main reason because it was raised.
No description provided by the author
ModuleError describes an error that belongs to a module.

# Type aliases

No description provided by the author