| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- package api
- import (
- "encoding/json"
- "net/http"
- "github.com/go-playground/validator/v10"
- "gorm.io/gorm"
- )
- // HTTPError is the object returned when the API encounters an error: this
- // gets marshaled into JSON
- type HTTPError struct {
- Code ErrorCode `json:"code"`
- Errors []string `json:"errors"`
- }
- // ErrorCode is a custom Porter error code, useful for frontend messages
- type ErrorCode int64
- var (
- // ErrorDataWrite describes an error in writing to the database
- ErrorDataWrite = HTTPError{
- Code: 500,
- Errors: []string{
- "could not write to database",
- },
- }
- // ErrorDataRead describes an error when reading from the database
- ErrorDataRead = HTTPError{
- Code: 500,
- Errors: []string{
- "could not read from database",
- },
- }
- // ErrorInternal describes a generic internal server error
- ErrorInternal = HTTPError{
- Code: 500,
- Errors: []string{
- "internal server error",
- },
- }
- )
- // ------------------------ Error helper functions ------------------------ //
- // sendExternalError marshals an HTTPError into JSON: this function will return an error if
- // a marshaling error occurs, but only after the internal error header has been sent to the
- // client.
- //
- // It then logs it via the app.logger and sends a formatted error to the client.
- func (app *App) sendExternalError(
- err error,
- code int,
- errExt HTTPError,
- w http.ResponseWriter,
- ) (intErr error) {
- respBytes, _ := json.Marshal(errExt)
- respBody := string(respBytes)
- app.logger.Warn().Err(err).
- Str("errExt", respBody).
- Msg("")
- w.WriteHeader(code)
- w.Write(respBytes)
- return nil
- }
- // handleErrorFormDecoding handles an error in decoding process from JSON to the
- // construction of a Form model, and the conversion between a form model and a
- // gorm.Model.
- func (app *App) handleErrorFormDecoding(err error, code ErrorCode, w http.ResponseWriter) {
- errExt := HTTPError{
- Code: code,
- Errors: []string{"could not process request"},
- }
- app.sendExternalError(err, http.StatusBadRequest, errExt, w)
- }
- // handleErrorFormValidation handles an error in the validation of form fields, and
- // sends a descriptive method about the incorrect fields to the client.
- func (app *App) handleErrorFormValidation(err error, code ErrorCode, w http.ResponseWriter) {
- // translate all validator errors
- errs := err.(validator.ValidationErrors)
- res := make([]string, 0)
- for _, field := range errs {
- valErr := field.Tag() + " validation failed"
- res = append(res, valErr)
- }
- errExt := HTTPError{
- Code: code,
- Errors: res,
- }
- app.sendExternalError(err, http.StatusUnprocessableEntity, errExt, w)
- }
- // handleErrorRead handles an error in reading a record from the DB. If the record is
- // not found, the error message is more descriptive; otherwise, a generic dataRead
- // error is sent.
- func (app *App) handleErrorRead(err error, code ErrorCode, w http.ResponseWriter) {
- // first check if the error is RecordNotFound -- send a more descriptive
- // message if that is the case
- if err == gorm.ErrRecordNotFound {
- errExt := HTTPError{
- Code: code,
- Errors: []string{"could not find requested object"},
- }
- app.sendExternalError(err, http.StatusNotFound, errExt, w)
- return
- }
- app.handleErrorDataRead(err, w)
- }
- // handleErrorDataWrite handles a database write error due to either a connection
- // error with the database or failure to write that wasn't caught by the validators
- func (app *App) handleErrorDataWrite(err error, w http.ResponseWriter) {
- app.sendExternalError(err, http.StatusInternalServerError, ErrorDataWrite, w)
- }
- // handleErrorDataRead handles a database read error due to an internal error, such as
- // the database connection or gorm internals
- func (app *App) handleErrorDataRead(err error, w http.ResponseWriter) {
- app.sendExternalError(err, http.StatusInternalServerError, ErrorDataRead, w)
- }
- // handleErrorInternalError is a catch-all for internal errors that occur during the
- // processing of a request
- func (app *App) handleErrorInternal(err error, w http.ResponseWriter) {
- app.sendExternalError(err, http.StatusInternalServerError, ErrorInternal, w)
- }
|