Skip to content

Commit

Permalink
openapi3filter: Fallback to string when decoding request parameters (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
fenollp committed Oct 12, 2022
1 parent fc05f1c commit 138bfa0
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 9 deletions.
5 changes: 4 additions & 1 deletion README.md
Expand Up @@ -4,7 +4,10 @@
[![Join Gitter Chat Channel -](https://badges.gitter.im/getkin/kin.svg)](https://gitter.im/getkin/kin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

# Introduction
A [Go](https://golang.org) project for handling [OpenAPI](https://www.openapis.org/) files. We target the latest OpenAPI version (currently 3), but the project contains support for older OpenAPI versions too.
A [Go](https://golang.org) project for handling [OpenAPI](https://www.openapis.org/) files. We target:
* [OpenAPI `v2.0`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md) (formerly known as Swagger)
* [OpenAPI `v3.0`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)
* [OpenAPI `v3.1`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md) Soon! [Tracking issue here.](https://github.com/getkin/kin-openapi/issues/230)

Licensed under the [MIT License](./LICENSE).

Expand Down
4 changes: 3 additions & 1 deletion openapi3/loader.go
Expand Up @@ -56,7 +56,9 @@ type Loader struct {

// NewLoader returns an empty Loader
func NewLoader() *Loader {
return &Loader{}
return &Loader{
Context: context.Background(),
}
}

func (loader *Loader) resetVisitedPathItemRefs() {
Expand Down
64 changes: 64 additions & 0 deletions openapi3filter/issue624_test.go
@@ -0,0 +1,64 @@
package openapi3filter

import (
"net/http"
"testing"

"github.com/stretchr/testify/require"

"github.com/getkin/kin-openapi/openapi3"
"github.com/getkin/kin-openapi/routers/gorillamux"
)

func TestIssue624(t *testing.T) {
loader := openapi3.NewLoader()
ctx := loader.Context
spec := `
openapi: 3.0.0
info:
version: 1.0.0
title: Sample API
paths:
/items:
get:
description: Returns a list of stuff
parameters:
- description: "test non object"
explode: true
style: form
in: query
name: test
required: false
content:
application/json:
schema:
anyOf:
- type: string
- type: integer
responses:
'200':
description: Successful response
`[1:]

doc, err := loader.LoadFromData([]byte(spec))
require.NoError(t, err)

err = doc.Validate(ctx)
require.NoError(t, err)

router, err := gorillamux.NewRouter(doc)
require.NoError(t, err)
httpReq, err := http.NewRequest(http.MethodGet, `/items?test=test1`, nil)
require.NoError(t, err)

route, pathParams, err := router.FindRoute(httpReq)
require.NoError(t, err)

requestValidationInput := &RequestValidationInput{
Request: httpReq,
PathParams: pathParams,
Route: route,
}
err = ValidateRequest(ctx, requestValidationInput)
require.NoError(t, err)
}
22 changes: 18 additions & 4 deletions openapi3filter/req_resp_decoder.go
Expand Up @@ -158,7 +158,10 @@ func decodeContentParameter(param *openapi3.Parameter, input *RequestValidationI
}

func defaultContentParameterDecoder(param *openapi3.Parameter, values []string) (
outValue interface{}, outSchema *openapi3.Schema, err error) {
outValue interface{},
outSchema *openapi3.Schema,
err error,
) {
// Only query parameters can have multiple values.
if len(values) > 1 && param.In != openapi3.ParameterInQuery {
err = fmt.Errorf("%s parameter %q cannot have multiple values", param.In, param.Name)
Expand All @@ -170,7 +173,6 @@ func defaultContentParameterDecoder(param *openapi3.Parameter, values []string)
err = fmt.Errorf("parameter %q expected to have content", param.Name)
return
}

// We only know how to decode a parameter if it has one content, application/json
if len(content) != 1 {
err = fmt.Errorf("multiple content types for parameter %q", param.Name)
Expand All @@ -184,16 +186,28 @@ func defaultContentParameterDecoder(param *openapi3.Parameter, values []string)
}
outSchema = mt.Schema.Value

unmarshal := func(encoded string) (decoded interface{}, err error) {
if err = json.Unmarshal([]byte(encoded), &decoded); err != nil {
const specialJSONChars = `[]{}":,`
if !strings.ContainsAny(encoded, specialJSONChars) {
// A string in a query parameter is not serialized with (double) quotes
// as JSON would expect, so let's fallback to that.
decoded, err = encoded, nil
}
}
return
}

if len(values) == 1 {
if err = json.Unmarshal([]byte(values[0]), &outValue); err != nil {
if outValue, err = unmarshal(values[0]); err != nil {
err = fmt.Errorf("error unmarshaling parameter %q", param.Name)
return
}
} else {
outArray := make([]interface{}, 0, len(values))
for _, v := range values {
var item interface{}
if err = json.Unmarshal([]byte(v), &item); err != nil {
if item, err = unmarshal(v); err != nil {
err = fmt.Errorf("error unmarshaling parameter %q", param.Name)
return
}
Expand Down
6 changes: 3 additions & 3 deletions openapi3filter/validate_request.go
Expand Up @@ -148,7 +148,9 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
value = schema.Default
req := input.Request
switch parameter.In {
// case openapi3.ParameterInPath: TODO: no idea how to handle this
case openapi3.ParameterInPath:
// Path parameters are required.
// Next check `parameter.Required && !found` will catch this.
case openapi3.ParameterInQuery:
q := req.URL.Query()
q.Add(parameter.Name, fmt.Sprintf("%v", value))
Expand All @@ -160,8 +162,6 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
Name: parameter.Name,
Value: fmt.Sprintf("%v", value),
})
default:
return fmt.Errorf("unsupported parameter's 'in': %s", parameter.In)
}
}

Expand Down

0 comments on commit 138bfa0

Please sign in to comment.