mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
ref: move httpapi.Reponse into codersdk (#2954)
This commit is contained in:
@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -50,42 +52,16 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// Response represents a generic HTTP response.
|
||||
type Response struct {
|
||||
// Message is an actionable message that depicts actions the request took.
|
||||
// These messages should be fully formed sentences with proper punctuation.
|
||||
// Examples:
|
||||
// - "A user has been created."
|
||||
// - "Failed to create a user."
|
||||
Message string `json:"message"`
|
||||
// Detail is a debug message that provides further insight into why the
|
||||
// action failed. This information can be technical and a regular golang
|
||||
// err.Error() text.
|
||||
// - "database: too many open connections"
|
||||
// - "stat: too many open files"
|
||||
Detail string `json:"detail,omitempty"`
|
||||
// Validations are form field-specific friendly error messages. They will be
|
||||
// shown on a form field in the UI. These can also be used to add additional
|
||||
// context if there is a set of errors in the primary 'Message'.
|
||||
Validations []Error `json:"validations,omitempty"`
|
||||
}
|
||||
|
||||
// Error represents a scoped error to a user input.
|
||||
type Error struct {
|
||||
Field string `json:"field" validate:"required"`
|
||||
Detail string `json:"detail" validate:"required"`
|
||||
}
|
||||
|
||||
// ResourceNotFound is intentionally vague. All 404 responses should be identical
|
||||
// to prevent leaking existence of resources.
|
||||
func ResourceNotFound(rw http.ResponseWriter) {
|
||||
Write(rw, http.StatusNotFound, Response{
|
||||
Write(rw, http.StatusNotFound, codersdk.Response{
|
||||
Message: "Resource not found or you do not have access to this resource",
|
||||
})
|
||||
}
|
||||
|
||||
func Forbidden(rw http.ResponseWriter) {
|
||||
Write(rw, http.StatusForbidden, Response{
|
||||
Write(rw, http.StatusForbidden, codersdk.Response{
|
||||
Message: "Forbidden.",
|
||||
})
|
||||
}
|
||||
@ -114,7 +90,7 @@ func Write(rw http.ResponseWriter, status int, response interface{}) {
|
||||
func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool {
|
||||
err := json.NewDecoder(r.Body).Decode(value)
|
||||
if err != nil {
|
||||
Write(rw, http.StatusBadRequest, Response{
|
||||
Write(rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Request body must be valid JSON.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
@ -123,21 +99,21 @@ func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool {
|
||||
err = validate.Struct(value)
|
||||
var validationErrors validator.ValidationErrors
|
||||
if errors.As(err, &validationErrors) {
|
||||
apiErrors := make([]Error, 0, len(validationErrors))
|
||||
apiErrors := make([]codersdk.ValidationError, 0, len(validationErrors))
|
||||
for _, validationError := range validationErrors {
|
||||
apiErrors = append(apiErrors, Error{
|
||||
apiErrors = append(apiErrors, codersdk.ValidationError{
|
||||
Field: validationError.Field(),
|
||||
Detail: fmt.Sprintf("Validation failed for tag %q with value: \"%v\"", validationError.Tag(), validationError.Value()),
|
||||
})
|
||||
}
|
||||
Write(rw, http.StatusBadRequest, Response{
|
||||
Write(rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Validation failed.",
|
||||
Validations: apiErrors,
|
||||
})
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
Write(rw, http.StatusInternalServerError, Response{
|
||||
Write(rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error validating request body payload.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd/httpapi"
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
@ -19,7 +20,7 @@ func TestWrite(t *testing.T) {
|
||||
t.Run("NoErrors", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
rw := httptest.NewRecorder()
|
||||
httpapi.Write(rw, http.StatusOK, httpapi.Response{
|
||||
httpapi.Write(rw, http.StatusOK, codersdk.Response{
|
||||
Message: "Wow.",
|
||||
})
|
||||
var m map[string]interface{}
|
||||
@ -71,7 +72,7 @@ func TestRead(t *testing.T) {
|
||||
|
||||
var validate toValidate
|
||||
require.False(t, httpapi.Read(rw, r, &validate))
|
||||
var v httpapi.Response
|
||||
var v codersdk.Response
|
||||
err := json.NewDecoder(rw.Body).Decode(&v)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, v.Validations, 1)
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/coder/coder/codersdk"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@ -17,19 +19,19 @@ import (
|
||||
type QueryParamParser struct {
|
||||
// Errors is the set of errors to return via the API. If the length
|
||||
// of this set is 0, there are no errors!.
|
||||
Errors []Error
|
||||
Errors []codersdk.ValidationError
|
||||
}
|
||||
|
||||
func NewQueryParamParser() *QueryParamParser {
|
||||
return &QueryParamParser{
|
||||
Errors: []Error{},
|
||||
Errors: []codersdk.ValidationError{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *QueryParamParser) Int(vals url.Values, def int, queryParam string) int {
|
||||
v, err := parseQueryParam(vals, strconv.Atoi, def, queryParam)
|
||||
if err != nil {
|
||||
p.Errors = append(p.Errors, Error{
|
||||
p.Errors = append(p.Errors, codersdk.ValidationError{
|
||||
Field: queryParam,
|
||||
Detail: fmt.Sprintf("Query param %q must be a valid integer (%s)", queryParam, err.Error()),
|
||||
})
|
||||
@ -47,7 +49,7 @@ func (p *QueryParamParser) UUIDorMe(vals url.Values, def uuid.UUID, me uuid.UUID
|
||||
func (p *QueryParamParser) UUID(vals url.Values, def uuid.UUID, queryParam string) uuid.UUID {
|
||||
v, err := parseQueryParam(vals, uuid.Parse, def, queryParam)
|
||||
if err != nil {
|
||||
p.Errors = append(p.Errors, Error{
|
||||
p.Errors = append(p.Errors, codersdk.ValidationError{
|
||||
Field: queryParam,
|
||||
Detail: fmt.Sprintf("Query param %q must be a valid uuid", queryParam),
|
||||
})
|
||||
@ -75,7 +77,7 @@ func (p *QueryParamParser) UUIDs(vals url.Values, def []uuid.UUID, queryParam st
|
||||
return ids, nil
|
||||
}, def, queryParam)
|
||||
if err != nil {
|
||||
p.Errors = append(p.Errors, Error{
|
||||
p.Errors = append(p.Errors, codersdk.ValidationError{
|
||||
Field: queryParam,
|
||||
Detail: fmt.Sprintf("Query param %q has invalid uuids: %q", queryParam, err.Error()),
|
||||
})
|
||||
@ -105,7 +107,7 @@ func (*QueryParamParser) Strings(vals url.Values, def []string, queryParam strin
|
||||
func ParseCustom[T any](parser *QueryParamParser, vals url.Values, def T, queryParam string, parseFunc func(v string) (T, error)) T {
|
||||
v, err := parseQueryParam(vals, parseFunc, def, queryParam)
|
||||
if err != nil {
|
||||
parser.Errors = append(parser.Errors, Error{
|
||||
parser.Errors = append(parser.Errors, codersdk.ValidationError{
|
||||
Field: queryParam,
|
||||
Detail: fmt.Sprintf("Query param %q has invalid value: %s", queryParam, err.Error()),
|
||||
})
|
||||
|
Reference in New Issue
Block a user