Merge pull request #22267 from containers/renovate/github.com-gorilla-schema-1.x

Update module github.com/gorilla/schema to v1.3.0
This commit is contained in:
openshift-merge-bot[bot]
2024-04-11 14:40:32 +00:00
committed by GitHub
8 changed files with 194 additions and 10 deletions

2
go.mod
View File

@ -52,7 +52,7 @@ require (
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2 github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1 github.com/gorilla/mux v1.8.1
github.com/gorilla/schema v1.2.1 github.com/gorilla/schema v1.3.0
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/hugelgupf/p9 v0.3.1-0.20230822151754-54f5c5530921 github.com/hugelgupf/p9 v0.3.1-0.20230822151754-54f5c5530921
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12

4
go.sum
View File

@ -310,8 +310,8 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM= github.com/gorilla/schema v1.3.0 h1:rbciOzXAx3IB8stEFnfTwO3sYa6EWlQk79XdyustPDA=
github.com/gorilla/schema v1.2.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/schema v1.3.0/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=

View File

@ -87,7 +87,32 @@ The supported field types in the struct are:
Unsupported types are simply ignored, however custom types can be registered to be converted. Unsupported types are simply ignored, however custom types can be registered to be converted.
More examples are available on the Gorilla website: https://www.gorillatoolkit.org/pkg/schema ## Setting Defaults
It is possible to set default values when encoding/decoding by using the `default` tag option. The value of `default` is applied when a field has a zero value, a pointer has a nil value, or a slice is empty.
```go
type Person struct {
Phone string `schema:"phone,default:+123456"` // custom name
Age int `schema:"age,default:21"`
Admin bool `schema:"admin,default:false"`
Balance float64 `schema:"balance,default:10.0"`
Friends []string `schema:friends,default:john|bob`
}
```
The `default` tag option is supported for the following types:
* bool
* float variants (float32, float64)
* int variants (int, int8, int16, int32, int64)
* uint variants (uint, uint8, uint16, uint32, uint64)
* string
* a slice of the above types. As shown in the example above, `|` should be used to separate between slice items.
* a pointer to one of the above types (pointer to slice and slice of pointers are not supported).
> [!NOTE]
> Because primitive types like int, float, bool, unint and their variants have their default (or zero) values set by Golang, it is not possible to distinguish them from a provided value when decoding/encoding form values. In this case, the value provided by the `default` option tag will be always applied. For example, let's assume that the value submitted in the form for `balance` is `0.0` then the default of `10.0` will be applied, even if `0.0` is part of the form data for the `balance` field. In such cases, it is highly recommended to use pointers to allow schema to distinguish between when a form field has no provided value and when a form has a value equal to the corresponding default set by Golang for a particular type. If the type of the `Balance` field above is changed to `*float64`, then the zero value would be `nil`. In this case, if the form data value for `balance` is `0.0`, then the default will not be applied.
## License ## License

View File

@ -197,6 +197,7 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel
isSliceOfStructs: isSlice && isStruct, isSliceOfStructs: isSlice && isStruct,
isAnonymous: field.Anonymous, isAnonymous: field.Anonymous,
isRequired: options.Contains("required"), isRequired: options.Contains("required"),
defaultValue: options.getDefaultOptionValue(),
} }
} }
@ -248,6 +249,7 @@ type fieldInfo struct {
// isAnonymous indicates whether the field is embedded in the struct. // isAnonymous indicates whether the field is embedded in the struct.
isAnonymous bool isAnonymous bool
isRequired bool isRequired bool
defaultValue string
} }
func (f *fieldInfo) paths(prefix string) []string { func (f *fieldInfo) paths(prefix string) []string {
@ -303,3 +305,13 @@ func (o tagOptions) Contains(option string) bool {
} }
return false return false
} }
func (o tagOptions) getDefaultOptionValue() string {
for _, s := range o {
if strings.HasPrefix(s, "default:") {
return strings.Split(s, ":")[1]
}
}
return ""
}

View File

@ -143,3 +143,80 @@ func convertUint64(value string) reflect.Value {
} }
return invalidValue return invalidValue
} }
func convertPointer(k reflect.Kind, value string) reflect.Value {
switch k {
case boolType:
if v := convertBool(value); v.IsValid() {
converted := v.Bool()
return reflect.ValueOf(&converted)
}
case float32Type:
if v := convertFloat32(value); v.IsValid() {
converted := float32(v.Float())
return reflect.ValueOf(&converted)
}
case float64Type:
if v := convertFloat64(value); v.IsValid() {
converted := float64(v.Float())
return reflect.ValueOf(&converted)
}
case intType:
if v := convertInt(value); v.IsValid() {
converted := int(v.Int())
return reflect.ValueOf(&converted)
}
case int8Type:
if v := convertInt8(value); v.IsValid() {
converted := int8(v.Int())
return reflect.ValueOf(&converted)
}
case int16Type:
if v := convertInt16(value); v.IsValid() {
converted := int16(v.Int())
return reflect.ValueOf(&converted)
}
case int32Type:
if v := convertInt32(value); v.IsValid() {
converted := int32(v.Int())
return reflect.ValueOf(&converted)
}
case int64Type:
if v := convertInt64(value); v.IsValid() {
converted := int64(v.Int())
return reflect.ValueOf(&converted)
}
case stringType:
if v := convertString(value); v.IsValid() {
converted := v.String()
return reflect.ValueOf(&converted)
}
case uintType:
if v := convertUint(value); v.IsValid() {
converted := uint(v.Uint())
return reflect.ValueOf(&converted)
}
case uint8Type:
if v := convertUint8(value); v.IsValid() {
converted := uint8(v.Uint())
return reflect.ValueOf(&converted)
}
case uint16Type:
if v := convertUint16(value); v.IsValid() {
converted := uint16(v.Uint())
return reflect.ValueOf(&converted)
}
case uint32Type:
if v := convertUint32(value); v.IsValid() {
converted := uint32(v.Uint())
return reflect.ValueOf(&converted)
}
case uint64Type:
if v := convertUint64(value); v.IsValid() {
converted := uint64(v.Uint())
return reflect.ValueOf(&converted)
}
}
return invalidValue
}

View File

@ -84,6 +84,7 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
errors[path] = UnknownKeyError{Key: path} errors[path] = UnknownKeyError{Key: path}
} }
} }
errors.merge(d.setDefaults(t, v))
errors.merge(d.checkRequired(t, src)) errors.merge(d.checkRequired(t, src))
if len(errors) > 0 { if len(errors) > 0 {
return errors return errors
@ -91,6 +92,76 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
return nil return nil
} }
//setDefaults sets the default values when the `default` tag is specified,
//default is supported on basic/primitive types and their pointers,
//nested structs can also have default tags
func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value) MultiError {
struc := d.cache.get(t)
if struc == nil {
// unexpect, cache.get never return nil
return MultiError{"default-" + t.Name(): errors.New("cache fail")}
}
errs := MultiError{}
for _, f := range struc.fields {
vCurrent := v.FieldByName(f.name)
if vCurrent.Type().Kind() == reflect.Struct && f.defaultValue == "" {
errs.merge(d.setDefaults(vCurrent.Type(), vCurrent))
} else if isPointerToStruct(vCurrent) && f.defaultValue == "" {
errs.merge(d.setDefaults(vCurrent.Elem().Type(), vCurrent.Elem()))
}
if f.defaultValue != "" && f.isRequired {
errs.merge(MultiError{"default-" + f.name: errors.New("required fields cannot have a default value")})
} else if f.defaultValue != "" && vCurrent.IsZero() && !f.isRequired {
if f.typ.Kind() == reflect.Struct {
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
} else if f.typ.Kind() == reflect.Slice {
vals := strings.Split(f.defaultValue, "|")
//check if slice has one of the supported types for defaults
if _, ok := builtinConverters[f.typ.Elem().Kind()]; !ok {
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
continue
}
defaultSlice := reflect.MakeSlice(f.typ, 0, cap(vals))
for _, val := range vals {
//this check is to handle if the wrong value is provided
if convertedVal := builtinConverters[f.typ.Elem().Kind()](val); convertedVal.IsValid() {
defaultSlice = reflect.Append(defaultSlice, convertedVal)
}
}
vCurrent.Set(defaultSlice)
} else if f.typ.Kind() == reflect.Ptr {
t1 := f.typ.Elem()
if t1.Kind() == reflect.Struct || t1.Kind() == reflect.Slice {
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
}
//this check is to handle if the wrong value is provided
if convertedVal := convertPointer(t1.Kind(), f.defaultValue); convertedVal.IsValid() {
vCurrent.Set(convertedVal)
}
} else {
//this check is to handle if the wrong value is provided
if convertedVal := builtinConverters[f.typ.Kind()](f.defaultValue); convertedVal.IsValid() {
vCurrent.Set(builtinConverters[f.typ.Kind()](f.defaultValue))
}
}
}
}
return errs
}
func isPointerToStruct(v reflect.Value) bool {
return !v.IsZero() && v.Type().Kind() == reflect.Ptr && v.Elem().Type().Kind() == reflect.Struct
}
// checkRequired checks whether required fields are empty // checkRequired checks whether required fields are empty
// //
// check type t recursively if t has struct fields. // check type t recursively if t has struct fields.

View File

@ -3,7 +3,6 @@ package schema
import ( import (
"errors" "errors"
"fmt" "fmt"
"log"
"reflect" "reflect"
"strconv" "strconv"
) )
@ -97,7 +96,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
if isValidStructPointer(v.Field(i)) && !e.hasCustomEncoder(v.Field(i).Type()) { if isValidStructPointer(v.Field(i)) && !e.hasCustomEncoder(v.Field(i).Type()) {
err := e.encode(v.Field(i).Elem(), dst) err := e.encode(v.Field(i).Elem(), dst)
if err != nil { if err != nil {
log.Fatal(err) errors[v.Field(i).Elem().Type().String()] = err
} }
continue continue
} }
@ -118,7 +117,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
if v.Field(i).Type().Kind() == reflect.Struct { if v.Field(i).Type().Kind() == reflect.Struct {
err := e.encode(v.Field(i), dst) err := e.encode(v.Field(i), dst)
if err != nil { if err != nil {
log.Fatal(err) errors[v.Field(i).Type().String()] = err
} }
continue continue
} }

2
vendor/modules.txt vendored
View File

@ -680,7 +680,7 @@ github.com/gorilla/handlers
# github.com/gorilla/mux v1.8.1 # github.com/gorilla/mux v1.8.1
## explicit; go 1.20 ## explicit; go 1.20
github.com/gorilla/mux github.com/gorilla/mux
# github.com/gorilla/schema v1.2.1 # github.com/gorilla/schema v1.3.0
## explicit; go 1.20 ## explicit; go 1.20
github.com/gorilla/schema github.com/gorilla/schema
# github.com/hashicorp/errwrap v1.1.0 # github.com/hashicorp/errwrap v1.1.0