Skip to content

Commit 6da871e

Browse files
authored
openapi3filter: apply default values of an array in a query param with exploded = false (#1054)
* issue1053: apply default values of an array in a query param with exploded set to false * issue1053: apply default values of an array in a query param with exploded set to false * issue1053: apply default values of an array in a query param with exploded set to false * add test case with explode set to true * refactor code
1 parent a34baf0 commit 6da871e

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

openapi3filter/issue991_test.go

+30-9
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import (
99
"github.com/stretchr/testify/require"
1010
)
1111

12-
func TestValidateRequestDefault(t *testing.T) {
13-
const spec = `
12+
func generateSpec(explode bool) string {
13+
explodeStr := "false"
14+
if explode {
15+
explodeStr = "true"
16+
}
17+
return `
1418
openapi: 3.0.0
1519
info:
1620
title: 'Validator'
@@ -29,6 +33,7 @@ components:
2933
in: query
3034
name: type
3135
required: false
36+
explode: ` + explodeStr + `
3237
description: Type parameter
3338
schema:
3439
type: array
@@ -43,9 +48,9 @@ components:
4348
- B
4449
- C
4550
`
51+
}
4652

47-
router := setupTestRouter(t, spec)
48-
53+
func TestValidateRequestDefault(t *testing.T) {
4954
type args struct {
5055
url string
5156
expected []string
@@ -55,42 +60,57 @@ components:
5560
args args
5661
expectedModification bool
5762
expectedErr error
63+
spec string
5864
}{
5965
{
60-
name: "Valid request without type parameters set",
66+
name: "Valid request without type parameters set and explode is false",
6167
args: args{
6268
url: "/category",
63-
expected: []string{"A", "B", "C"},
69+
expected: []string{"A,B,C"},
6470
},
6571
expectedModification: false,
6672
expectedErr: nil,
73+
spec: generateSpec(false),
6774
},
6875
{
69-
name: "Valid request with 1 type parameters set",
76+
name: "Valid request with 1 type parameters set and explode is false",
7077
args: args{
7178
url: "/category?type=A",
7279
expected: []string{"A"},
7380
},
7481
expectedModification: false,
7582
expectedErr: nil,
83+
spec: generateSpec(false),
7684
},
7785
{
78-
name: "Valid request with 2 type parameters set",
86+
name: "Valid request with 2 type parameters set and explode is false",
7987
args: args{
8088
url: "/category?type=A&type=C",
8189
expected: []string{"A", "C"},
8290
},
8391
expectedModification: false,
8492
expectedErr: nil,
93+
spec: generateSpec(false),
8594
},
8695
{
87-
name: "Valid request with 1 type parameters set out of enum",
96+
name: "Valid request with 1 type parameters set out of enum and explode is false",
8897
args: args{
8998
url: "/category?type=X",
9099
expected: nil,
91100
},
92101
expectedModification: false,
93102
expectedErr: &RequestError{},
103+
spec: generateSpec(false),
104+
},
105+
{
106+
name: "Valid request without type parameters set and explode is true",
107+
args: args{
108+
url: "/category",
109+
expected: []string{"A", "B", "C"},
110+
},
111+
expectedModification: false,
112+
expectedErr: nil,
113+
spec: generateSpec(true),
94114
},
95115
}
96116
for _, tc := range tests {
@@ -99,6 +119,7 @@ components:
99119
req, err := http.NewRequest(http.MethodGet, tc.args.url, nil)
100120
require.NoError(t, err)
101121

122+
router := setupTestRouter(t, tc.spec)
102123
route, pathParams, err := router.FindRoute(req)
103124
require.NoError(t, err)
104125

openapi3filter/validate_request.go

+17-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"net/url"
1111
"sort"
12+
"strings"
1213

1314
"github.com/getkin/kin-openapi/openapi3"
1415
)
@@ -111,15 +112,26 @@ func appendToQueryValues[T any](q url.Values, parameterName string, v []T) {
111112
}
112113
}
113114

115+
func joinValues(values []any, sep string) string {
116+
strValues := make([]string, len(values))
117+
for i, v := range values {
118+
strValues[i] = fmt.Sprintf("%v", v)
119+
}
120+
return strings.Join(strValues, sep)
121+
}
122+
114123
// populateDefaultQueryParameters populates default values inside query parameters, while ensuring types are respected
115-
func populateDefaultQueryParameters(q url.Values, parameterName string, value any) {
124+
func populateDefaultQueryParameters(q url.Values, parameterName string, value any, explode bool) {
116125
switch t := value.(type) {
117126
case []any:
118-
appendToQueryValues(q, parameterName, t)
127+
if explode {
128+
appendToQueryValues(q, parameterName, t)
129+
} else {
130+
q.Add(parameterName, joinValues(t, ","))
131+
}
119132
default:
120133
q.Add(parameterName, fmt.Sprintf("%v", value))
121134
}
122-
123135
}
124136

125137
// ValidateParameter validates a parameter's value by JSON schema.
@@ -175,7 +187,8 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
175187
// Next check `parameter.Required && !found` will catch this.
176188
case openapi3.ParameterInQuery:
177189
q := req.URL.Query()
178-
populateDefaultQueryParameters(q, parameter.Name, value)
190+
explode := parameter.Explode != nil && *parameter.Explode
191+
populateDefaultQueryParameters(q, parameter.Name, value, explode)
179192
req.URL.RawQuery = q.Encode()
180193
case openapi3.ParameterInHeader:
181194
req.Header.Add(parameter.Name, fmt.Sprintf("%v", value))

0 commit comments

Comments
 (0)