Improve and Fix Header Parsing#943
Conversation
We previously only iterated over struct fields if the struct belonged to a slice. We now iterate all struct fields regardless of whether it's a single object or part of a slice.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #943 +/- ##
==========================================
- Coverage 93.16% 93.11% -0.06%
==========================================
Files 23 23
Lines 4918 4968 +50
==========================================
+ Hits 4582 4626 +44
- Misses 272 275 +3
- Partials 64 67 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@leonklingele if you get a chance, I'd love your thoughts on this :) |
|
@wolveix sorry for the very late reply. This lgtm! Let's get this merged? |
|
Hey @wolveix, I'm now in a situation where I'd benefit from this change 😄 Is there anything still missing here? |
|
I kinda wanted @danielgtaylor's feedback on it before merging 😅 |
findHeaders' depth check (len(i) > 1) dropped headers for fields promoted via embedded structs that had no explicit header tag, a silent behavior change from prior releases and inconsistent with Go field promotion (a promoted field stopped behaving like a top-level field). Replace the depth check with isPromotedField, which auto-names a header from the field name only for surface-level fields: literal top-level fields and fields reachable purely through embedded structs. Named nested struct fields still require an explicit header tag (the new feature). Add a regression test covering all three cases.
With params recursion enabled, a param declared on a pointer-nested input struct (e.g. Filters *Filters with a query tag) was documented in the OpenAPI spec but never populated at runtime: the intermediate pointer is nil and the traversal skipped it, so the advertised param could never be received. Add findResult.EveryAlloc, which allocates nil pointers along the path so nested fields can be set. A pointer it allocates is reset to nil when no value below it was set, so an absent optional group stays nil instead of becoming an empty struct. Use it for both the main and multipart input param population paths. Add tests covering value- and pointer-nested population and the absent-group-stays-nil case.
…sing # Conflicts: # huma.go # huma_test.go
The public input path always populates a freshly-zeroed input, so slice and map elements along a param path are empty and the slice/map branches in everyAlloc never iterate at runtime, leaving them uncovered. Add a white-box test that drives everyAlloc directly over populated slices/maps, an allocated-and-kept pointer, an allocated-then-rolled-back pointer, a reused pointer, and an unsupported kind.
|
@leonklingele I updated this PR, would appreciate your review :) |
| // tag instead. | ||
| func isPromotedField(root reflect.Type, path []int) bool { | ||
| t := baseType(root) | ||
| for _, idx := range path[:len(path)-1] { |
There was a problem hiding this comment.
Can path ever be empty? 🤔
This comment was marked as low quality.
This comment was marked as low quality.
| *HiddenHeaders `hidden:"true"` | ||
|
|
||
| // Hidden slice field to exercise hidden-walk across slice -> elem. | ||
| HiddenSlice []HiddenHeadersSliceElem `hidden:"true"` |
There was a problem hiding this comment.
Will this also hide nested structs?
type Auth struct {
Token string `header:"X-Token"`
}
type Input struct {
Auth Auth `hidden:"true"` // <-- I'd expect this to be hidden
}
There was a problem hiding this comment.
Does this really fix #811?
processInputType still only detects a request body via a top-level lookup:
Line 1433 in f7f7e18
So this shape still will not populate Input.Body:
type Inner struct {
Body struct {
V int `json:"v"`
}
}
type Blended struct {
RequestAware
Input Inner
}(See #811 (comment))
| } | ||
|
|
||
| set := false | ||
| switch current.Kind() { |
There was a problem hiding this comment.
What about arrays? 😅
type Header struct {
Value string `header:"X-Value"`
}
type Resp struct {
Headers [1]Header
}
#944 was merged as a successor to the original intention of this PR.
This PR enables recursion for
findInTypeforfindHeadersandfindParams. I'm still exploring the implications of doing this, though my initial findings don't turn up anything negative. The main purpose of this is to allow named fields in input and output structs, which have been requested features for quite some time.This PR: