Skip to content

Commit

Permalink
fix: do not zero the field if not tagged for query nor path (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fazt01 authored Mar 28, 2023
1 parent 1467a92 commit aed39bf
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 18 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ How to release a new version:

## [Unreleased]

## [0.6.1] - 2022-03-28
### Changed
- package `http/param` does not zero the field if not tagged with any relevant tags

## [0.6.0] - 2023-03-03
### Added
- package `http/signature` to simplify defining http handler functions
Expand Down Expand Up @@ -45,7 +49,8 @@ How to release a new version:
### Added
- Added Changelog.

[Unreleased]: https://github.com/strvcom/strv-backend-go-net/compare/v0.6.0...HEAD
[Unreleased]: https://github.com/strvcom/strv-backend-go-net/compare/v0.6.1...HEAD
[0.6.1]: https://github.com/strvcom/strv-backend-go-net/compare/v0.6.0...v0.6.1
[0.6.0]: https://github.com/strvcom/strv-backend-go-net/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/strvcom/strv-backend-go-net/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/strvcom/strv-backend-go-net/compare/v0.3.0...v0.4.0
Expand Down
48 changes: 31 additions & 17 deletions http/param/param.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,28 +106,46 @@ func (p Parser) Parse(r *http.Request, dest any) error {
continue
}
valueField := v.Field(i)
// Zero the value, even if it would not be set by following path or query parameter.
// This will cause potential partial result from previous parser (e.g. json.Unmarshal) to be discarded on
// fields that are tagged for path or query parameter.
valueField.Set(reflect.Zero(typeField.Type))
tag := typeField.Tag
err := p.parseQueryParam(r, tag, valueField)
err := p.parseParam(r, typeField, valueField)
if err != nil {
return err
}
err = p.parsePathParam(r, tag, valueField)
}
return nil
}

func (p Parser) parseParam(r *http.Request, typeField reflect.StructField, v reflect.Value) error {
tag := typeField.Tag
pathParamName, okPath := p.PathParamTagResolver(tag)
queryParamName, okQuery := p.QueryParamTagResolver(tag)
if !okPath && !okQuery {
// do nothing if tagged neither for query nor param
return nil
}

// Zero the value, even if it would not be set by following path or query parameter.
// This will cause potential partial result from previous parser (e.g. json.Unmarshal) to be discarded on
// fields that are tagged for path or query parameter.
v.Set(reflect.Zero(typeField.Type))

if okPath {
err := p.parsePathParam(r, pathParamName, v)
if err != nil {
return err
}
}

if okQuery {
err := p.parseQueryParam(r, queryParamName, v)
if err != nil {
return err
}
}

return nil
}

func (p Parser) parsePathParam(r *http.Request, tag reflect.StructTag, v reflect.Value) error {
paramName, ok := p.PathParamTagResolver(tag)
if !ok {
return nil
}
func (p Parser) parsePathParam(r *http.Request, paramName string, v reflect.Value) error {
if p.PathParamFunc == nil {
return fmt.Errorf("struct's field was tagged for parsing the path parameter (%s) but PathParamFunc to get value of path parameter is not defined", paramName)
}
Expand All @@ -141,11 +159,7 @@ func (p Parser) parsePathParam(r *http.Request, tag reflect.StructTag, v reflect
return nil
}

func (p Parser) parseQueryParam(r *http.Request, tag reflect.StructTag, v reflect.Value) error {
paramName, ok := p.QueryParamTagResolver(tag)
if !ok {
return nil
}
func (p Parser) parseQueryParam(r *http.Request, paramName string, v reflect.Value) error {
query := r.URL.Query()
if values, ok := query[paramName]; ok && len(values) > 0 {
err := unmarshalValueOrSlice(values, v)
Expand Down
20 changes: 20 additions & 0 deletions http/param/param_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,26 @@ func TestParser_Parse_PathParam_FuncNotDefinedError(t *testing.T) {
assert.Error(t, err)
}

type otherFieldsStruct struct {
Q string `param:"query=q"`
Other string `json:"other"`
}

func TestParser_Parse_DoesNotOverwrite(t *testing.T) {
p := DefaultParser()
req := httptest.NewRequest(http.MethodGet, "https://test.com/hello?q=input", nil)
expected := otherFieldsStruct{
Q: "input",
Other: "already filled",
}

result := otherFieldsStruct{Other: "already filled"}
err := p.Parse(req, &result)

assert.NoError(t, err)
assert.Equal(t, expected, result)
}

type variousTagsStruct struct {
A string `key:"location=val"`
B string `key:"location=val=excessive"`
Expand Down

0 comments on commit aed39bf

Please sign in to comment.