Skip to content

Commit

Permalink
Merge pull request #718 from skwair/fix-double-validation-errors
Browse files Browse the repository at this point in the history
fix: double validation errors
  • Loading branch information
danielgtaylor authored Feb 4, 2025
2 parents 21a771d + 644be26 commit f9ffb6a
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 23 deletions.
15 changes: 15 additions & 0 deletions huma.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I)
pv, err := parseInto(ctx, f, value, nil, *p)
if err != nil {
res.Add(pb, value, err.Error())
return
}

if !op.SkipValidateParams {
Expand Down Expand Up @@ -1474,6 +1475,20 @@ func parseSliceInto(f reflect.Value, values []string) (any, error) {
f.Set(reflect.ValueOf(vs))
return vs, nil

case reflect.Uint8:
vs, err := parseArrElement(values, func(s string) (uint8, error) {
val, err := strconv.ParseUint(s, 10, 8)
if err != nil {
return 0, err
}
return uint8(val), nil
})
if err != nil {
return nil, errors.New("invalid integer")
}
f.Set(reflect.ValueOf(vs))
return vs, nil

case reflect.Uint16:
vs, err := parseArrElement(values, func(s string) (uint16, error) {
val, err := strconv.ParseUint(s, 10, 16)
Expand Down
57 changes: 34 additions & 23 deletions huma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,21 +452,22 @@ func TestFeatures(t *testing.T) {
Method: http.MethodGet,
Path: "/test-params/{int}/{uuid}",
}, func(ctx context.Context, input *struct {
PathInt string `path:"int"`
PathInt int `path:"int"`
PathUUID UUID `path:"uuid"`
QueryInt int `query:"int"`
QueryFloat float32 `query:"float"`
QueryBefore time.Time `query:"before"`
QueryDate time.Time `query:"date" timeFormat:"2006-01-02"`
QueryURL url.URL `query:"url"`
QueryUint uint32 `query:"uint"`
QueryUint uint `query:"uint"`
QueryBool bool `query:"bool"`
QueryInts []int `query:"ints"`
QueryInts8 []int8 `query:"ints8"`
QueryInts16 []int16 `query:"ints16"`
QueryInts32 []int32 `query:"ints32"`
QueryInts64 []int64 `query:"ints64"`
QueryUints []uint `query:"uints"`
QueryUints8 []uint8 `query:"uints8"`
QueryUints16 []uint16 `query:"uints16"`
QueryUints32 []uint32 `query:"uints32"`
QueryUints64 []uint64 `query:"uints64"`
Expand All @@ -479,30 +480,40 @@ func TestFeatures(t *testing.T) {
})
},
Method: http.MethodGet,
URL: "/test-params/bad/not-a-uuid?int=bad&float=bad&before=bad&date=bad&url=:&uint=bad&bool=bad&ints=bad&ints8=bad&ints16=bad&ints32=bad&ints64=bad&uints=bad&uints16=bad&uints32=bad&uints64=bad&floats32=bad&floats64=bad",
URL: "/test-params/bad/not-a-uuid?int=bad&float=bad&before=bad&date=bad&url=:&uint=bad&bool=bad&ints=bad&ints8=bad&ints16=bad&ints32=bad&ints64=bad&uints=bad&uints8=bad&uints16=bad&uints32=bad&uints64=bad&floats32=bad&floats64=bad",
Assert: func(t *testing.T, resp *httptest.ResponseRecorder) {
assert.Equal(t, http.StatusUnprocessableEntity, resp.Code)

assert.Contains(t, resp.Body.String(), "invalid integer")
assert.Contains(t, resp.Body.String(), "invalid value: invalid UUID length: 10")
assert.Contains(t, resp.Body.String(), "invalid float")
assert.Contains(t, resp.Body.String(), "invalid date/time")
assert.Contains(t, resp.Body.String(), "invalid url.URL")
assert.Contains(t, resp.Body.String(), "invalid bool")
assert.Contains(t, resp.Body.String(), "required query parameter is missing")
assert.Contains(t, resp.Body.String(), "required header parameter is missing")

assert.Contains(t, resp.Body.String(), "query.ints")
assert.Contains(t, resp.Body.String(), "query.ints8")
assert.Contains(t, resp.Body.String(), "query.ints16")
assert.Contains(t, resp.Body.String(), "query.ints32")
assert.Contains(t, resp.Body.String(), "query.ints64")
assert.Contains(t, resp.Body.String(), "query.uints")
assert.Contains(t, resp.Body.String(), "query.uints16")
assert.Contains(t, resp.Body.String(), "query.uints32")
assert.Contains(t, resp.Body.String(), "query.uints64")
assert.Contains(t, resp.Body.String(), "query.floats32")
assert.Contains(t, resp.Body.String(), "query.floats64")
var body struct {
Errors []huma.ErrorDetail `json:"errors"`
}
err := json.Unmarshal(resp.Body.Bytes(), &body)
require.NoError(t, err)

assert.Len(t, body.Errors, 23)
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "path.int", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid value: invalid UUID length: 10", Location: "path.uuid", Value: "not-a-uuid"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.int", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid float", Location: "query.float", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid date/time for format 2006-01-02T15:04:05.999999999Z07:00", Location: "query.before", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid date/time for format 2006-01-02", Location: "query.date", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid url.URL value", Location: "query.url", Value: ":"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.uint", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid boolean", Location: "query.bool", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.ints", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.ints8", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.ints16", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.ints32", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.ints64", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.uints", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.uints8", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.uints16", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.uints32", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid integer", Location: "query.uints64", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid floating value", Location: "query.floats32", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "invalid floating value", Location: "query.floats64", Value: "bad"})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "required query parameter is missing", Location: "query.req", Value: ""})
assert.Contains(t, body.Errors, huma.ErrorDetail{Message: "required header parameter is missing", Location: "header.req", Value: ""})
},
},
{
Expand Down

0 comments on commit f9ffb6a

Please sign in to comment.