Skip to content

Commit

Permalink
Merge pull request #545 from danielgtaylor/precompute-msgs
Browse files Browse the repository at this point in the history
fix: automatically precompute schema validation messages
  • Loading branch information
danielgtaylor authored Aug 17, 2024
2 parents b13a422 + 7be3b0c commit 912f02e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 3 deletions.
13 changes: 12 additions & 1 deletion huma.go
Original file line number Diff line number Diff line change
Expand Up @@ -692,8 +692,19 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I)
}
}

if op.RequestBody != nil {
for _, mediatype := range op.RequestBody.Content {
if mediatype.Schema != nil {
// Ensure all schema validation errors are set up properly as some
// parts of the schema may have been user-supplied.
mediatype.Schema.PrecomputeMessages()
}
}
}

var inSchema *Schema
if op.RequestBody != nil && op.RequestBody.Content != nil && op.RequestBody.Content["application/json"] != nil && op.RequestBody.Content["application/json"].Schema != nil {
hasInputBody = true
inSchema = op.RequestBody.Content["application/json"].Schema
}

Expand Down Expand Up @@ -1263,7 +1274,7 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I)
}
}

if hasInputBody {
if hasInputBody && len(inputBodyIndex) > 0 {
// We need to get the body into the correct type now that it has been
// validated. Benchmarks on Go 1.20 show that using `json.Unmarshal` a
// second time is faster than `mapstructure.Decode` or any of the other
Expand Down
39 changes: 38 additions & 1 deletion huma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1687,7 +1687,6 @@ Content of example2.txt.
},
},
}
customSchema.PrecomputeMessages()

huma.Register(api, huma.Operation{
Method: http.MethodPut,
Expand Down Expand Up @@ -2160,6 +2159,44 @@ func TestSchemaWithExample(t *testing.T) {
assert.Equal(t, 1, example)
}

func TestCustomSchemaErrors(t *testing.T) {
// Ensure that custom schema errors are correctly reported without having
// to manually call `schema.PrecomputeMessages()`.
_, api := humatest.New(t, huma.DefaultConfig("Test API", "1.0.0"))

huma.Register(api, huma.Operation{
OperationID: "test",
Method: http.MethodPost,
Path: "/test",
RequestBody: &huma.RequestBody{
Content: map[string]*huma.MediaType{
"application/json": {
Schema: &huma.Schema{
Type: huma.TypeObject,
Required: []string{"test"},
AdditionalProperties: false,
Properties: map[string]*huma.Schema{
"test": {
Type: huma.TypeInteger,
Minimum: Ptr(10.0),
},
},
},
},
},
},
}, func(ctx context.Context, input *struct {
RawBody []byte
}) (*struct{}, error) {
return nil, nil
})

resp := api.Post("/test", map[string]any{"test": 1})

assert.Equal(t, http.StatusUnprocessableEntity, resp.Result().StatusCode)
assert.Contains(t, resp.Body.String(), `expected number \u003e= 10`)
}

func TestBodyRace(t *testing.T) {
// Run with the following:
// go test -run=TestBodyRace -race -parallel=100
Expand Down
4 changes: 3 additions & 1 deletion schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,9 @@ func schemaFromType(r Registry, t reflect.Type) *Schema {
v := reflect.New(t).Interface()
if sp, ok := v.(SchemaProvider); ok {
// Special case: type provides its own schema. Do not try to generate.
return sp.Schema(r)
custom := sp.Schema(r)
custom.PrecomputeMessages()
return custom
}

// Handle special cases.
Expand Down

0 comments on commit 912f02e

Please sign in to comment.