mirror of
https://github.com/ggicci/httpin.git
synced 2025-02-21 19:06:46 +02:00
Merge pull request #107 from alecsammon/omitempty
Add support for `omitempty` field tag to exclude empty values from queries and headers
This commit is contained in:
commit
eed0621e7f
@ -56,6 +56,7 @@ type ListUsersInput struct {
|
||||
Page int `in:"query=page;default=1"`
|
||||
PerPage int `in:"query=per_page;default=20"`
|
||||
IsMember bool `in:"query=is_member"`
|
||||
Search *string `in:"query=search;omitempty"`
|
||||
}
|
||||
|
||||
func ListUsers(rw http.ResponseWriter, r *http.Request) {
|
||||
|
@ -17,6 +17,7 @@ func init() {
|
||||
RegisterDirective("default", &DirectiveDefault{})
|
||||
RegisterDirective("nonzero", &DirectiveNonzero{})
|
||||
registerDirective("path", defaultPathDirective)
|
||||
registerDirective("omitempty", &DirectiveOmitEmpty{})
|
||||
|
||||
// decoder is a special executor which does nothing, but is an indicator of
|
||||
// overriding the decoder for a specific field.
|
||||
|
@ -1,12 +1,20 @@
|
||||
package core
|
||||
|
||||
import "github.com/ggicci/httpin/internal"
|
||||
import (
|
||||
"github.com/ggicci/httpin/internal"
|
||||
)
|
||||
|
||||
type FormEncoder struct {
|
||||
Setter func(key string, value []string) // form value setter
|
||||
}
|
||||
|
||||
func (e *FormEncoder) Execute(rtm *DirectiveRuntime) error {
|
||||
if rtm.Value.IsZero() {
|
||||
if rtm.Resolver.GetDirective("omitempty") != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if rtm.IsFieldSet() {
|
||||
return nil // skip when already encoded by former directives
|
||||
}
|
||||
|
@ -29,13 +29,15 @@ func TestDirectiveHeader_Decode(t *testing.T) {
|
||||
|
||||
func TestDirectiveHeader_NewRequest(t *testing.T) {
|
||||
type ApiQuery struct {
|
||||
ApiUid int `in:"header=x-api-uid"`
|
||||
ApiToken string `in:"header=X-Api-Token"`
|
||||
ApiUid int `in:"header=x-api-uid;omitempty"`
|
||||
ApiToken *string `in:"header=X-Api-Token;omitempty"`
|
||||
}
|
||||
|
||||
t.Run("with all values", func(t *testing.T) {
|
||||
tk := "some-secret-token"
|
||||
query := &ApiQuery{
|
||||
ApiUid: 91241844,
|
||||
ApiToken: "some-secret-token",
|
||||
ApiToken: &tk,
|
||||
}
|
||||
|
||||
co, err := New(ApiQuery{})
|
||||
@ -48,4 +50,26 @@ func TestDirectiveHeader_NewRequest(t *testing.T) {
|
||||
expected.Header.Set("x-api-uid", "91241844")
|
||||
expected.Header.Set("X-Api-Token", "some-secret-token")
|
||||
assert.Equal(t, expected, req)
|
||||
})
|
||||
|
||||
t.Run("with empty value", func(t *testing.T) {
|
||||
query := &ApiQuery{
|
||||
ApiUid: 0,
|
||||
ApiToken: nil,
|
||||
}
|
||||
|
||||
co, err := New(ApiQuery{})
|
||||
assert.NoError(t, err)
|
||||
req, err := co.NewRequest("POST", "/api", query)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected, _ := http.NewRequest("POST", "/api", nil)
|
||||
assert.Equal(t, expected, req)
|
||||
|
||||
_, ok := req.Header["X-Api-Uid"]
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = req.Header["X-Api-Token"]
|
||||
assert.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
17
core/omitempty.go
Normal file
17
core/omitempty.go
Normal file
@ -0,0 +1,17 @@
|
||||
// directive: "omitempty"
|
||||
// https://ggicci.github.io/httpin/directives/omitempty
|
||||
|
||||
package core
|
||||
|
||||
// DirectiveOmitEmpty is used with the DirectiveQuery, DirectiveForm, and DirectiveHeader to indicate that the field
|
||||
// should be omitted when the value is empty.
|
||||
// It does not have any affect when used by itself
|
||||
type DirectiveOmitEmpty struct{}
|
||||
|
||||
func (*DirectiveOmitEmpty) Decode(_ *DirectiveRuntime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*DirectiveOmitEmpty) Encode(_ *DirectiveRuntime) error {
|
||||
return nil
|
||||
}
|
@ -33,7 +33,7 @@ func TestDirectiveQuery_Decode(t *testing.T) {
|
||||
func TestDirectiveQuery_NewRequest(t *testing.T) {
|
||||
type SearchQuery struct {
|
||||
Name string `in:"query=name"`
|
||||
Age int `in:"query=age"`
|
||||
Age int `in:"query=age;omitempty"`
|
||||
Enabled bool `in:"query=enabled"`
|
||||
Price float64 `in:"query=price"`
|
||||
|
||||
@ -41,8 +41,10 @@ func TestDirectiveQuery_NewRequest(t *testing.T) {
|
||||
AgeList []int `in:"query=age_list[]"`
|
||||
|
||||
NamePointer *string `in:"query=name_pointer"`
|
||||
AgePointer *int `in:"query=age_pointer"`
|
||||
AgePointer *int `in:"query=age_pointer;omitempty"`
|
||||
}
|
||||
|
||||
t.Run("with all values", func(t *testing.T) {
|
||||
query := &SearchQuery{
|
||||
Name: "cupcake",
|
||||
Age: 12,
|
||||
@ -77,6 +79,22 @@ func TestDirectiveQuery_NewRequest(t *testing.T) {
|
||||
expectedQuery.Set("age_pointer", "19") // query.PointerAge
|
||||
expected.URL.RawQuery = expectedQuery.Encode()
|
||||
assert.Equal(t, expected, req)
|
||||
})
|
||||
|
||||
t.Run("with empty values", func(t *testing.T) {
|
||||
query := &SearchQuery{}
|
||||
|
||||
co, err := New(SearchQuery{})
|
||||
assert.NoError(t, err)
|
||||
req, err := co.NewRequest("GET", "/pets", query)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, req.URL.Query().Has("name"))
|
||||
assert.False(t, req.URL.Query().Has("age"))
|
||||
|
||||
assert.True(t, req.URL.Query().Has("name_pointer"))
|
||||
assert.False(t, req.URL.Query().Has("age_pointer"))
|
||||
})
|
||||
}
|
||||
|
||||
type Location struct {
|
||||
|
Loading…
x
Reference in New Issue
Block a user