Skip to content

Commit

Permalink
Add YAML support (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
Allenzhli authored Nov 11, 2021
1 parent f2ebb45 commit 7556578
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 22 deletions.
6 changes: 6 additions & 0 deletions bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ func Test_Bind(t *testing.T) {
}
})

Convey("Bind YAML", func() {
for _, testCase := range yamlTestCases {
performYamlTest(t, Bind, testCase)
}
})

Convey("Bind multipart form", func() {
for _, testCase := range multipartFormTestCases {
performMultipartFormTest(t, Bind, testCase)
Expand Down
33 changes: 33 additions & 0 deletions binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"github.com/unknwon/com"
"gopkg.in/macaron.v1"
"gopkg.in/yaml.v3"
)

func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {
Expand All @@ -43,6 +44,8 @@ func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {
_, _ = ctx.Invoke(MultipartForm(obj, ifacePtr...))
case strings.Contains(contentType, "json"):
_, _ = ctx.Invoke(Json(obj, ifacePtr...))
case strings.Contains(contentType, "yaml"):
_, _ = ctx.Invoke(Yaml(obj, ifacePtr...))
default:
var errors Errors
if contentType == "" {
Expand All @@ -60,6 +63,7 @@ func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {

const (
_JSON_CONTENT_TYPE = "application/json; charset=utf-8"
_YAML_CONTENT_TYPE = "text/yaml; charset=utf-8"
STATUS_UNPROCESSABLE_ENTITY = 422
)

Expand Down Expand Up @@ -207,10 +211,39 @@ func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
errors.Add([]string{}, ERR_DESERIALIZATION, err.Error())
}
}
if errors != nil {
ctx.Map(errors)
return
}
validateAndMap(jsonStruct, ctx, errors, ifacePtr...)
}
}

// Yaml is middleware to deserialize a YAML payload from the request
// into the struct that is passed in. The resulting struct is then
// validated, but no error handling is actually performed here.
// An interface pointer can be added as a second argument in order
// to map the struct to a specific interface.
func Yaml(yamlStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
return func(ctx *macaron.Context) {
var errors Errors
ensureNotPointer(yamlStruct)
yamlStruct := reflect.New(reflect.TypeOf(yamlStruct))
if ctx.Req.Request.Body != nil {
defer ctx.Req.Request.Body.Close()
err := yaml.NewDecoder(ctx.Req.Request.Body).Decode(yamlStruct.Interface())
if err != nil && err != io.EOF {
errors.Add([]string{}, ERR_DESERIALIZATION, err.Error())
}
}
if errors != nil {
ctx.Map(errors)
return
}
validateAndMap(yamlStruct, ctx, errors, ifacePtr...)
}
}

// URL is the middleware to parse URL parameters into struct fields.
func URL(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
return func(ctx *macaron.Context) {
Expand Down
22 changes: 11 additions & 11 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@ import (
type (
// For basic test cases with a required field
Post struct {
Title string `form:"title" json:"title" binding:"Required"`
Content string `form:"content" json:"content"`
Title string `form:"title" json:"title" yaml:"title" binding:"Required"`
Content string `form:"content" json:"content" yaml:"content"`
}

// To be used as a nested struct (with a required field)
Person struct {
Name string `form:"name" json:"name" binding:"Required"`
Email string `form:"email" json:"email"`
Name string `form:"name" json:"name" yaml:"name" binding:"Required"`
Email string `form:"email" json:"email" yaml:"email"`
}

// For advanced test cases: multiple values, embedded
// and nested structs, an ignored field, and single
// and multiple file uploads
BlogPost struct {
Post
Id int `binding:"Required"` // JSON not specified here for test coverage
Ignored string `form:"-" json:"-"`
Ratings []int `form:"rating" json:"ratings"`
Author Person `json:"author"`
Coauthor *Person `json:"coauthor"`
Id int `binding:"Required"` // JSON and YAML not specified here for test coverage
Ignored string `form:"-" json:"-" yaml:"-"`
Ratings []int `form:"rating" json:"ratings" yaml:"ratings"`
Author Person `json:"author" yaml:"author"`
Coauthor *Person `json:"coauthor" yaml:"coauthor"`
HeaderImage *multipart.FileHeader
Pictures []*multipart.FileHeader `form:"picture"`
unexported string `form:"unexported"` //nolint
Expand Down Expand Up @@ -79,8 +79,8 @@ type (
}

Group struct {
Name string `json:"name" binding:"Required"`
People []Person `json:"people" binding:"MinSize(1)"`
Name string `json:"name" yaml:"name" binding:"Required"`
People []Person `json:"people" yaml:"people" binding:"MinSize(1)"`
}

UrlForm struct {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ go 1.12
require (
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e
golang.org/x/tools v0.0.0-20190805222050-c5a2fd39b72a // indirect
gopkg.in/macaron.v1 v1.3.5
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8
)
14 changes: 4 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,15 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49N
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190805222050-c5a2fd39b72a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs=
gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI=
gopkg.in/macaron.v1 v1.3.5 h1:FUA16VFBojxzfU75KqWrV/6BPv9O2R1GnybSGRie9QQ=
gopkg.in/macaron.v1 v1.3.5/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8 h1:tH9C0MON9YI3/KuD+u5+tQrQQ8px0MrcJ/avzeALw7o=
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 7556578

Please sign in to comment.