openmeter / api /v3 /apierrors /errors_ctors.go
Leon4gr45's picture
Upload folder using huggingface_hub (part 3)
04f1444 verified
Raw
History Blame Contribute Delete
9.69 kB
package apierrors
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/go-chi/chi/v5/middleware"
)
const (
// Content Type
ContentTypeKey = "Content-Type"
ContentTypeProblemValue = "application/problem+json"
// Internal
InternalType = "https://kongapi.info/konnect/internal"
InternalTitle = "Internal"
InternalDetail = "An internal failure occurred"
// Service Unavailable
UnavailableType = "https://kongapi.info/konnect/unavailable"
UnavailableTitle = "Unavailable"
UnavailableDetail = "The requested service is unavailable"
// NotImplemented
NotImplementedType = "https://kongapi.info/konnect/not-implemented"
NotImplementedTitle = "Not Implemented"
NotImplementedDetail = "The requested functionality is not implemented"
// Unauthenticated
UnauthenticatedType = "https://kongapi.info/konnect/unauthenticated"
UnauthenticatedTitle = "Unauthenticated"
UnauthenticatedDetail = "A valid token is required"
// Forbidden
ForbiddenType = "https://kongapi.info/konnect/unauthorized"
ForbiddenTitle = "Forbidden"
ForbiddenDetail = "Permission denied"
// NotFound
NotFoundType = "https://kongapi.info/konnect/not-found"
NotFoundTitle = "Not Found"
NotFoundDetail = "The requested %s was not found"
NotFoundDetails = "The requested %s were not found: %v"
// Method Not Allowed
MethodNotAllowedType = "https://kongapi.info/konnect/method-not-allowed"
MethodNotAllowedTitle = "Method Not Allowed"
MethodNotAllowedDetail = "The requested method is not allowed"
// BadRequest
BadRequestType = "https://kongapi.info/konnect/bad-request"
BadRequestTitle = "Bad Request"
// Gone
GoneType = "https://kongapi.info/konnect/gone"
GoneTitle = "Gone"
GoneDetail = "The requested resource is no longer available"
// Precondition Failed
PreconditionFailedType = "https://kongapi.info/konnect/precondition-failed"
PreconditionFailedTitle = "Precondition Failed"
// Rate Limit
RateLimitType = "https://kongapi.info/konnect/rate-limited"
RateLimitTitle = "Rate Limited"
RateLimitDetail = "Too many requests"
// Conflict
ConflictType = "https://kongapi.info/konnect/resource-conflict"
ConflictTitle = "Conflict"
// Empty Set
EmptySetType = "Empty Set"
EmptySetCursorType = "Empty Set Cursor"
)
// NewInternalError generates an internal server error.
func NewInternalError(ctx context.Context, err error) *BaseAPIError {
return &BaseAPIError{
Type: InternalType,
Status: http.StatusInternalServerError,
Title: InternalTitle,
Instance: instance(ctx),
Detail: InternalDetail,
UnderlyingError: err,
ctx: ctx,
}
}
// NewServiceUnavailable generates a not found error.
func NewServiceUnavailable(ctx context.Context, err error) *BaseAPIError {
return &BaseAPIError{
Type: UnavailableType,
Status: http.StatusServiceUnavailable,
Title: UnavailableTitle,
Instance: instance(ctx),
Detail: UnavailableDetail,
UnderlyingError: err,
ctx: ctx,
}
}
// NewUnauthenticatedError generates an unauthenticated error.
func NewUnauthenticatedError(ctx context.Context, err error) *BaseAPIError {
return &BaseAPIError{
Type: UnauthenticatedType,
Status: http.StatusUnauthorized,
Title: UnauthenticatedTitle,
Instance: instance(ctx),
Detail: UnauthenticatedDetail,
UnderlyingError: err,
ctx: ctx,
}
}
// NewForbiddenError generates an unauthorized error.
func NewForbiddenError(ctx context.Context, err error) *BaseAPIError {
return &BaseAPIError{
Type: ForbiddenType,
Status: http.StatusForbidden,
Title: ForbiddenTitle,
Instance: instance(ctx),
Detail: ForbiddenDetail,
UnderlyingError: err,
ctx: ctx,
}
}
// NewForbiddenErrorDetail generates an forbidden error with a user readable/detailed message.
func NewForbiddenErrorDetail(ctx context.Context, detailMessage string) *BaseAPIError {
return &BaseAPIError{
Type: ForbiddenType,
Status: http.StatusForbidden,
Title: ForbiddenTitle,
Instance: instance(ctx),
Detail: MakeSentenceCase(detailMessage),
ctx: ctx,
}
}
// NewNotFoundError generates a not found error.
func NewNotFoundError(ctx context.Context, err error, entityType string) *BaseAPIError {
if entityType != "" {
return &BaseAPIError{
Type: NotFoundType,
Status: http.StatusNotFound,
Title: NotFoundTitle,
Instance: instance(ctx),
Detail: fmt.Sprintf(NotFoundDetail, entityType),
UnderlyingError: err,
ctx: ctx,
}
}
return &BaseAPIError{
Type: NotFoundType,
Status: http.StatusNotFound,
Title: NotFoundTitle,
Instance: instance(ctx),
UnderlyingError: err,
ctx: ctx,
}
}
// NewNotFoundErrors generates a not found error for multiple resources.
func NewNotFoundErrors(
ctx context.Context,
err error,
entityType string,
resources any,
) *BaseAPIError {
return &BaseAPIError{
Type: NotFoundType,
Status: http.StatusNotFound,
Title: NotFoundTitle,
Instance: instance(ctx),
Detail: fmt.Sprintf(NotFoundDetails, entityType, resources),
UnderlyingError: err,
ctx: ctx,
}
}
// NewMethodNotAllowedError generates a method not allowed error.
func NewMethodNotAllowedError(ctx context.Context) *BaseAPIError {
return &BaseAPIError{
Type: MethodNotAllowedType,
Status: http.StatusMethodNotAllowed,
Title: MethodNotAllowedTitle,
Instance: instance(ctx),
Detail: MethodNotAllowedDetail,
ctx: ctx,
}
}
// NewBadRequestError generates a bad request error.
func NewBadRequestError(ctx context.Context, err error, invalidFields InvalidParameters) *BaseAPIError {
detail := BadRequestTitle
if len(invalidFields) > 0 {
detail = fmt.Sprintf("%s: %s", BadRequestTitle, invalidFields.String())
}
return &BaseAPIError{
Type: BadRequestType,
Status: http.StatusBadRequest,
Title: BadRequestTitle,
Instance: instance(ctx),
InvalidParameters: invalidFields,
UnderlyingError: err,
Detail: detail,
ctx: ctx,
}
}
// NewUnsupportedSortFieldError generates a 400 for a `sort` query value naming a
// field the endpoint does not support. v3 list endpoints accept only their
// declared snake_case sort fields; the reason lists the supported ones. Returned
// directly by a resource's sort-field converter so handlers can propagate it
// without re-wrapping.
func NewUnsupportedSortFieldError(ctx context.Context, field string, supported ...string) *BaseAPIError {
err := fmt.Errorf("unsupported sort field: %s", field)
return NewBadRequestError(ctx, err, InvalidParameters{
{
Field: "sort",
Reason: fmt.Sprintf(
"unsupported sort field %q, supported fields: %s",
field, strings.Join(supported, ", "),
),
Source: InvalidParamSourceQuery,
},
})
}
// NewGoneError generates a gone error.
func NewGoneError(ctx context.Context, err error) *BaseAPIError {
return &BaseAPIError{
Type: GoneType,
Status: http.StatusGone,
Title: GoneTitle,
Instance: instance(ctx),
Detail: GoneDetail,
UnderlyingError: err,
ctx: ctx,
}
}
// NewPreconditionFailedError generates an precondition failed error.
func NewPreconditionFailedError(ctx context.Context, precondition string) *BaseAPIError {
return &BaseAPIError{
Type: PreconditionFailedType,
Status: http.StatusPreconditionFailed,
Title: PreconditionFailedTitle,
Instance: instance(ctx),
Detail: MakeSentenceCase(precondition),
UnderlyingError: fmt.Errorf("precondition failed: %s", precondition),
ctx: ctx,
}
}
// NewRateLimitError generates an HTTP 429 Too Many Requests error.
func NewRateLimitError(ctx context.Context) *BaseAPIError {
return &BaseAPIError{
Type: RateLimitType,
Status: http.StatusTooManyRequests,
Title: RateLimitTitle,
Instance: instance(ctx),
Detail: RateLimitDetail,
ctx: ctx,
}
}
func NewConflictError(ctx context.Context, err error, detail string) *BaseAPIError {
return &BaseAPIError{
Type: ConflictType,
Status: http.StatusConflict,
Title: ConflictTitle,
Instance: instance(ctx),
Detail: detail,
UnderlyingError: err,
ctx: ctx,
}
}
func NewEmptySetResponse(ctx context.Context, cursorPagination bool) *BaseAPIError {
bae := &BaseAPIError{
Status: http.StatusOK,
ctx: ctx,
}
if cursorPagination {
bae.Type = EmptySetCursorType
} else {
bae.Type = EmptySetType
}
return bae
}
func NewNotImplementedError(ctx context.Context, err error) *BaseAPIError {
return &BaseAPIError{
Type: NotImplementedType,
Status: http.StatusNotImplemented,
Title: NotImplementedTitle,
Instance: instance(ctx),
Detail: NotImplementedDetail,
UnderlyingError: err,
ctx: ctx,
}
}
// MakeSentenceCase takes any string and returns a Sentence case version of it
func MakeSentenceCase(msg string) string {
if msg == "" {
return ""
}
return strings.ToUpper(msg[:1]) + msg[1:]
}
// instance returns the request ID from the context
// TODO: use trace ID from context instead
func instance(ctx context.Context) string {
reqID := middleware.GetReqID(ctx)
if reqID != "" {
return fmt.Sprintf("urn:request:%s", reqID)
}
return ""
}