77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
package utilities
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgconn"
|
|
"github.com/jackc/pgerrcode"
|
|
)
|
|
|
|
// PostgresError is a custom error struct for marshalling Postgres errors to JSON.
|
|
type PostgresError struct {
|
|
Code string `json:"code"`
|
|
HttpStatusCode int `json:"-"`
|
|
Message string `json:"message"`
|
|
Hint string `json:"hint,omitempty"`
|
|
Detail string `json:"detail,omitempty"`
|
|
}
|
|
|
|
// NewPostgresError returns a new PostgresError if the error was from a publicly
|
|
// accessible Postgres error.
|
|
func NewPostgresError(err error) *PostgresError {
|
|
var pgErr *pgconn.PgError
|
|
if errors.As(err, &pgErr) && isPubliclyAccessiblePostgresError(pgErr.Code) {
|
|
return &PostgresError{
|
|
Code: pgErr.Code,
|
|
HttpStatusCode: getHttpStatusCodeFromPostgresErrorCode(pgErr.Code),
|
|
Message: pgErr.Message,
|
|
Detail: pgErr.Detail,
|
|
Hint: pgErr.Hint,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (pg *PostgresError) IsUniqueConstraintViolated() bool {
|
|
// See https://www.postgresql.org/docs/current/errcodes-appendix.html for list of error codes
|
|
return pg.Code == "23505"
|
|
}
|
|
|
|
// isPubliclyAccessiblePostgresError checks if the Postgres error should be
|
|
// made accessible.
|
|
func isPubliclyAccessiblePostgresError(code string) bool {
|
|
if len(code) != 5 {
|
|
return false
|
|
}
|
|
|
|
// default response
|
|
return getHttpStatusCodeFromPostgresErrorCode(code) != 0
|
|
}
|
|
|
|
// getHttpStatusCodeFromPostgresErrorCode maps a Postgres error code to a HTTP
|
|
// status code. Returns 0 if the code doesn't map to a given postgres error code.
|
|
func getHttpStatusCodeFromPostgresErrorCode(code string) int {
|
|
if code == pgerrcode.RaiseException ||
|
|
code == pgerrcode.IntegrityConstraintViolation ||
|
|
code == pgerrcode.RestrictViolation ||
|
|
code == pgerrcode.NotNullViolation ||
|
|
code == pgerrcode.ForeignKeyViolation ||
|
|
code == pgerrcode.UniqueViolation ||
|
|
code == pgerrcode.CheckViolation ||
|
|
code == pgerrcode.ExclusionViolation {
|
|
return 500
|
|
}
|
|
|
|
// Use custom HTTP status code if Postgres error was triggered with `PTXXX`
|
|
// code. This is consistent with PostgREST's behaviour as well.
|
|
if strings.HasPrefix(code, "PT") {
|
|
if httpStatusCode, err := strconv.ParseInt(code[2:], 10, 0); err == nil {
|
|
return int(httpStatusCode)
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|