From 62bf0f764655ea170bf8ccc32fba323598123d0e Mon Sep 17 00:00:00 2001 From: podhmo Date: Sun, 10 Dec 2023 04:43:41 +0900 Subject: [PATCH] feat: support default value binding with allOfSchema (#885) --- openapi3filter/issue884_test.go | 113 +++++++++++++++++++++++++++++ openapi3filter/validate_request.go | 43 ++++++----- 2 files changed, 139 insertions(+), 17 deletions(-) create mode 100644 openapi3filter/issue884_test.go diff --git a/openapi3filter/issue884_test.go b/openapi3filter/issue884_test.go new file mode 100644 index 000000000..ea6e461c0 --- /dev/null +++ b/openapi3filter/issue884_test.go @@ -0,0 +1,113 @@ +package openapi3filter + +import ( + "net/http" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/routers/gorillamux" +) + +func TestIssue884(t *testing.T) { + loader := openapi3.NewLoader() + ctx := loader.Context + spec := ` + openapi: 3.0.0 + info: + version: 1.0.0 + title: Sample API + components: + schemas: + TaskSortEnum: + enum: + - createdAt + - -createdAt + - updatedAt + - -updatedAt + paths: + /tasks: + get: + operationId: ListTask + parameters: + - in: query + name: withDefault + schema: + allOf: + - $ref: '#/components/schemas/TaskSortEnum' + - default: -createdAt + - in: query + name: withoutDefault + schema: + allOf: + - $ref: '#/components/schemas/TaskSortEnum' + - in: query + name: withManyDefaults + schema: + allOf: + - default: -updatedAt + - $ref: '#/components/schemas/TaskSortEnum' + - default: -createdAt + 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) + + tests := []struct { + name string + options *Options + expectedQuery url.Values + }{ + { + name: "no defaults are added to requests", + options: &Options{ + SkipSettingDefaults: true, + }, + expectedQuery: url.Values{}, + }, + + { + name: "defaults are added to requests", + expectedQuery: url.Values{ + "withDefault": []string{"-createdAt"}, + "withManyDefaults": []string{"-updatedAt"}, // first default is win + }, + }, + } + + for _, testcase := range tests { + t.Run(testcase.name, func(t *testing.T) { + httpReq, err := http.NewRequest(http.MethodGet, "/tasks", nil) + require.NoError(t, err) + httpReq.Header.Set("Content-Type", "application/json") + require.NoError(t, err) + + route, pathParams, err := router.FindRoute(httpReq) + require.NoError(t, err) + + requestValidationInput := &RequestValidationInput{ + Request: httpReq, + PathParams: pathParams, + Route: route, + Options: testcase.options, + } + err = ValidateRequest(ctx, requestValidationInput) + require.NoError(t, err) + + q := httpReq.URL.Query() + assert.Equal(t, testcase.expectedQuery, q) + }) + } +} diff --git a/openapi3filter/validate_request.go b/openapi3filter/validate_request.go index 02ff4736e..7a8b5ca11 100644 --- a/openapi3filter/validate_request.go +++ b/openapi3filter/validate_request.go @@ -139,24 +139,33 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param } // Set default value if needed - if !options.SkipSettingDefaults && value == nil && schema != nil && schema.Default != nil { + if !options.SkipSettingDefaults && value == nil && schema != nil { value = schema.Default - req := input.Request - switch parameter.In { - 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)) - req.URL.RawQuery = q.Encode() - case openapi3.ParameterInHeader: - req.Header.Add(parameter.Name, fmt.Sprintf("%v", value)) - case openapi3.ParameterInCookie: - req.AddCookie(&http.Cookie{ - Name: parameter.Name, - Value: fmt.Sprintf("%v", value), - }) + for _, subSchema := range schema.AllOf { + if subSchema.Value.Default != nil { + value = subSchema.Value.Default + break // This is not a validation of the schema itself, so use the first default value. + } + } + + if value != nil { + req := input.Request + switch parameter.In { + 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)) + req.URL.RawQuery = q.Encode() + case openapi3.ParameterInHeader: + req.Header.Add(parameter.Name, fmt.Sprintf("%v", value)) + case openapi3.ParameterInCookie: + req.AddCookie(&http.Cookie{ + Name: parameter.Name, + Value: fmt.Sprintf("%v", value), + }) + } } }