Skip to content

Commit

Permalink
feat: allow making query/header params required
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgtaylor committed Oct 22, 2023
1 parent e25de1a commit c13d251
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 10 deletions.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,14 @@ Inputs and outputs are **always** structs that represent the entirety of the inc

Requests can have parameters and/or a body as input to the handler function. Inputs use standard Go structs with special fields and/or tags. Here are the available tags:

| Tag | Description | Example |
| -------- | ---------------------------------- | ------------------------ |
| `path` | Name of the path parameter | `path:"thing-id"` |
| `query` | Name of the query string parameter | `query:"q"` |
| `header` | Name of the header parameter | `header:"Authorization"` |
| Tag | Description | Example |
| ---------- | ------------------------------------- | ------------------------ |
| `path` | Name of the path parameter | `path:"thing-id"` |
| `query` | Name of the query string parameter | `query:"q"` |
| `header` | Name of the header parameter | `header:"Authorization"` |
| `required` | Mark a query/header param as required | `required:"true"` |

> :whale: The `required` tag is discouraged and is only used for query/header params, which should generally be optional for clients to send.

The following parameter types are supported out of the box:

Expand Down
15 changes: 10 additions & 5 deletions huma.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type paramFieldInfo struct {
Type reflect.Type
Name string
Loc string
Required bool
Default string
TimeFormat string
Schema *Schema
Expand All @@ -96,12 +97,11 @@ func findParams(registry Registry, op *Operation, t reflect.Type) *findResult[*p
}

name := ""
required := false
var explode *bool
if p := f.Tag.Get("path"); p != "" {
pfi.Loc = "path"
name = p
required = true
pfi.Required = true
} else if q := f.Tag.Get("query"); q != "" {
pfi.Loc = "query"
name = q
Expand All @@ -116,6 +116,11 @@ func findParams(registry Registry, op *Operation, t reflect.Type) *findResult[*p
return nil
}

// While discouraged, make it possible to make query/header params required.
if r := f.Tag.Get("required"); r == "true" {
pfi.Required = true
}

pfi.Name = name

if f.Type == timeType {
Expand All @@ -135,7 +140,7 @@ func findParams(registry Registry, op *Operation, t reflect.Type) *findResult[*p
Name: name,
In: pfi.Loc,
Explode: explode,
Required: required,
Required: pfi.Required,
Schema: pfi.Schema,
Example: example,
})
Expand Down Expand Up @@ -567,9 +572,9 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I)
value = p.Default
}

if p.Loc == "path" && value == "" {
if p.Required && value == "" {
// Path params are always required.
res.Add(pb, "", "required path parameter is missing")
res.Add(pb, "", "required "+p.Loc+" parameter is missing")
return
}

Expand Down
4 changes: 4 additions & 0 deletions huma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ func TestFeatures(t *testing.T) {
QueryDate time.Time `query:"date" timeFormat:"2006-01-02"`
QueryUint uint32 `query:"uint"`
QueryBool bool `query:"bool"`
QueryReq bool `query:"req" required:"true"`
HeaderReq string `header:"req" required:"true"`
}) (*struct{}, error) {
return nil, nil
})
Expand All @@ -202,6 +204,8 @@ func TestFeatures(t *testing.T) {
assert.Contains(t, resp.Body.String(), "invalid float")
assert.Contains(t, resp.Body.String(), "invalid date/time")
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")
},
},
{
Expand Down

0 comments on commit c13d251

Please sign in to comment.