chatdesk-ui/auth_v2.169.0/internal/utilities/postgres.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
}