Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openapi3filter: Fallback to string when decoding request parameters #631

Merged
merged 5 commits into from Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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