mirror of https://github.com/artifacthub/hub.git
126 lines
3.6 KiB
Go
126 lines
3.6 KiB
Go
package helpers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/artifacthub/hub/internal/hub"
|
|
)
|
|
|
|
const (
|
|
// DefaultAPICacheMaxAge represents the default cache duration used by some
|
|
// API endpoints.
|
|
DefaultAPICacheMaxAge = 5 * time.Minute
|
|
|
|
// PaginationDefaultLimit represents the default limit used for pagination.
|
|
PaginationDefaultLimit = 20
|
|
|
|
// PaginationMaxLimit represents the default maximum limit used for
|
|
// pagination.
|
|
PaginationMaxLimit = 60
|
|
|
|
// PaginationTotalCount represents a header used to indicate the number of
|
|
// entries available for pagination purposes.
|
|
PaginationTotalCount = "Pagination-Total-Count"
|
|
)
|
|
|
|
// BuildCacheControlHeader builds an http cache header using the max age
|
|
// duration provided.
|
|
func BuildCacheControlHeader(cacheMaxAge time.Duration) string {
|
|
return fmt.Sprintf("max-age=%d", int64(cacheMaxAge.Seconds()))
|
|
}
|
|
|
|
// GetPagination is a helper that extracts the pagination information from the
|
|
// query string values provided.
|
|
func GetPagination(qs url.Values, defaultLimit, maxLimit int) (*hub.Pagination, error) {
|
|
// Limit
|
|
var limit int
|
|
if qs.Get("limit") != "" {
|
|
var err error
|
|
limit, err = strconv.Atoi(qs.Get("limit"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid limit: %s", qs.Get("limit"))
|
|
}
|
|
if limit > maxLimit {
|
|
return nil, fmt.Errorf("invalid limit: %s (max: %d)", qs.Get("limit"), maxLimit)
|
|
}
|
|
} else {
|
|
limit = defaultLimit
|
|
}
|
|
|
|
// Offset
|
|
var offset int
|
|
if qs.Get("offset") != "" {
|
|
var err error
|
|
offset, err = strconv.Atoi(qs.Get("offset"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid offset: %s", qs.Get("offset"))
|
|
}
|
|
}
|
|
|
|
return &hub.Pagination{
|
|
Limit: limit,
|
|
Offset: offset,
|
|
}, nil
|
|
}
|
|
|
|
// RenderJSON is a helper to write the json data provided to the given http
|
|
// response writer, setting the appropriate content type, cache and status code.
|
|
func RenderJSON(w http.ResponseWriter, dataJSON []byte, cacheMaxAge time.Duration, code int) {
|
|
w.Header().Set("Cache-Control", BuildCacheControlHeader(cacheMaxAge))
|
|
w.Header().Set("Content-Length", strconv.Itoa(len(dataJSON)))
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(code)
|
|
_, _ = w.Write(dataJSON)
|
|
}
|
|
|
|
// RenderErrorJSON is a helper to write the error provided to the given http
|
|
// response writer as json setting the appropriate content type.
|
|
func RenderErrorJSON(w http.ResponseWriter, err error) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
var errMsg string
|
|
switch {
|
|
case errors.Is(err, hub.ErrInvalidInput):
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err != nil {
|
|
errMsg = err.Error()
|
|
}
|
|
case errors.Is(err, hub.ErrInsufficientPrivilege):
|
|
w.WriteHeader(http.StatusForbidden)
|
|
case errors.Is(err, hub.ErrNotFound):
|
|
w.WriteHeader(http.StatusNotFound)
|
|
default:
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
}
|
|
writeErrorJSON(w, errMsg)
|
|
}
|
|
|
|
// RenderErrorWithCodeJSON is a helper to write the error provided to the given
|
|
// http response writer as json setting the appropriate content type. Unlike
|
|
// RenderErrorJSON, which decides what status code to use based on the type of
|
|
// the error provided, this methods expects the status code and the error msg
|
|
// will be always sent to the requester.
|
|
func RenderErrorWithCodeJSON(w http.ResponseWriter, err error, code int) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(code)
|
|
var errMsg string
|
|
if err != nil {
|
|
errMsg = err.Error()
|
|
}
|
|
writeErrorJSON(w, errMsg)
|
|
}
|
|
|
|
// writeErrorJSON buids the error payload and writes it to the writer provided.
|
|
func writeErrorJSON(w io.Writer, msg string) {
|
|
data := map[string]interface{}{
|
|
"message": msg,
|
|
}
|
|
_ = json.NewEncoder(w).Encode(data)
|
|
}
|