diff --git a/parameters/cookie_parameters.go b/parameters/cookie_parameters.go index 888d29e..0ff4323 100644 --- a/parameters/cookie_parameters.go +++ b/parameters/cookie_parameters.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/pb33f/libopenapi-validator/errors" "github.com/pb33f/libopenapi-validator/helpers" - "github.com/pb33f/libopenapi-validator/paths" "github.com/pb33f/libopenapi/datamodel/high/base" v3 "github.com/pb33f/libopenapi/datamodel/high/v3" "net/http" @@ -15,24 +14,7 @@ import ( "strings" ) -func (v *paramValidator) ValidateCookieParams(request *http.Request) (bool, []*errors.ValidationError) { - - // find path - var pathItem *v3.PathItem - var foundPath string - var errs []*errors.ValidationError - - if v.pathItem == nil { - pathItem, errs, foundPath = paths.FindPath(request, v.document) - if pathItem == nil || errs != nil { - v.errors = errs - return false, errs - } - } else { - pathItem = v.pathItem - foundPath = v.pathValue - } - +func (v *paramValidator) ValidateCookieParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { // extract params for the operation var params = helpers.ExtractParamsForOperation(request, pathItem) var validationErrors []*errors.ValidationError @@ -125,7 +107,7 @@ func (v *paramValidator) ValidateCookieParams(request *http.Request) (bool, []*e } } - errors.PopulateValidationErrors(validationErrors, request, foundPath) + errors.PopulateValidationErrors(validationErrors, request, pathValue) if len(validationErrors) > 0 { return false, validationErrors diff --git a/parameters/cookie_parameters_test.go b/parameters/cookie_parameters_test.go index 34bf329..eadf1aa 100644 --- a/parameters/cookie_parameters_test.go +++ b/parameters/cookie_parameters_test.go @@ -5,10 +5,10 @@ package parameters import ( "github.com/pb33f/libopenapi" - "github.com/pb33f/libopenapi-validator/paths" "github.com/stretchr/testify/assert" "net/http" "testing" + "github.com/pb33f/libopenapi-validator/paths" ) func TestNewValidator_CookieNoPath(t *testing.T) { @@ -26,14 +26,11 @@ paths: doc, _ := libopenapi.NewDocument([]byte(spec)) m, _ := doc.BuildV3Model() - v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/I/do/not/exist", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "1"}) - valid, errors := v.ValidateCookieParams(request) - - assert.False(t, valid) + _, errors, _ := paths.FindPath(request, &m.Model) assert.Len(t, errors, 1) assert.Equal(t, request.Method, errors[0].RequestMethod) assert.Equal(t, request.URL.Path, errors[0].RequestPath) @@ -59,8 +56,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "1"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -85,8 +83,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "123.455"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -111,8 +110,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "false"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -138,8 +138,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "true"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -168,8 +169,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "chicken"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -198,8 +200,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "milk"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -229,8 +232,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "12345"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -263,8 +267,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "pink,true,number,2"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -296,8 +301,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "pink,2,number,2"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -325,8 +331,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "2,3,4"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -353,8 +360,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "2,true,4,'hello'"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -381,8 +389,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "true,false,true,false,true"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -409,8 +418,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "true,1,hey,ho"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -437,8 +447,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "true,false,pb33f,false,99.99"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -465,8 +476,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "true,false,0,false,1"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -492,8 +504,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "2"}) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -519,40 +532,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "2500"}) // too many dude. - - valid, errors := v.ValidateCookieParams(request) - - assert.False(t, valid) - assert.Len(t, errors, 1) - assert.Equal(t, "Instead of '2500', use one of the allowed values: '1, 2, 99'", errors[0].HowToFix) -} - -func TestNewValidator_PresetPath(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /burgers/beef: - get: - parameters: - - name: PattyPreference - in: cookie - required: true - schema: - type: integer - enum: [1, 2, 99]` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - m, _ := doc.BuildV3Model() - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/beef", nil) - request.AddCookie(&http.Cookie{Name: "PattyPreference", Value: "2500"}) // too many dude. - - // preset the path - path, _, pv := paths.FindPath(request, &m.Model) - v.SetPathItem(path, pv) - - valid, errors := v.ValidateCookieParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateCookieParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) diff --git a/parameters/header_parameters.go b/parameters/header_parameters.go index 2eb02b5..3f4f753 100644 --- a/parameters/header_parameters.go +++ b/parameters/header_parameters.go @@ -11,27 +11,11 @@ import ( "github.com/pb33f/libopenapi-validator/errors" "github.com/pb33f/libopenapi-validator/helpers" - "github.com/pb33f/libopenapi-validator/paths" "github.com/pb33f/libopenapi/datamodel/high/base" v3 "github.com/pb33f/libopenapi/datamodel/high/v3" ) -func (v *paramValidator) ValidateHeaderParams(request *http.Request) (bool, []*errors.ValidationError) { - // find path - var pathItem *v3.PathItem - var specPath string - var errs []*errors.ValidationError - if v.pathItem == nil { - pathItem, errs, specPath = paths.FindPath(request, v.document) - if pathItem == nil || errs != nil { - v.errors = errs - return false, errs - } - } else { - pathItem = v.pathItem - specPath = v.pathValue - } - +func (v *paramValidator) ValidateHeaderParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { // extract params for the operation params := helpers.ExtractParamsForOperation(request, pathItem) @@ -145,7 +129,7 @@ func (v *paramValidator) ValidateHeaderParams(request *http.Request) (bool, []*e } } - errors.PopulateValidationErrors(validationErrors, request, specPath) + errors.PopulateValidationErrors(validationErrors, request, pathValue) if len(validationErrors) > 0 { return false, validationErrors diff --git a/parameters/header_parameters_test.go b/parameters/header_parameters_test.go index 69f3bc2..7ab573b 100644 --- a/parameters/header_parameters_test.go +++ b/parameters/header_parameters_test.go @@ -5,10 +5,10 @@ package parameters import ( "github.com/pb33f/libopenapi" - "github.com/pb33f/libopenapi-validator/paths" "github.com/stretchr/testify/assert" "net/http" "testing" + "github.com/pb33f/libopenapi-validator/paths" ) func TestNewValidator_HeaderParamMissing(t *testing.T) { @@ -30,8 +30,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/bish/bosh", nil) - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) @@ -41,36 +42,6 @@ paths: assert.Equal(t, "/bish/bosh", errors[0].SpecPath) } -func TestNewValidator_HeaderPathMissing(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /bish/bosh: - get: - parameters: - - name: bash - in: header - required: true - schema: - type: string -` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - m, _ := doc.BuildV3Model() - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodGet, "https://things.com/I/do/not/exist", nil) - - valid, errors := v.ValidateHeaderParams(request) - - assert.False(t, valid) - assert.Equal(t, 1, len(errors)) - assert.Equal(t, "GET Path '/I/do/not/exist' not found", errors[0].Message) - assert.Equal(t, request.Method, errors[0].RequestMethod) - assert.Equal(t, request.URL.Path, errors[0].RequestPath) - assert.Equal(t, "", errors[0].SpecPath) -} - func TestNewValidator_HeaderParamDefaultEncoding_InvalidParamTypeNumber(t *testing.T) { spec := `openapi: 3.1.0 @@ -90,8 +61,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "two") // headers are case-insensitive - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) @@ -117,8 +89,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "two") // headers are case-insensitive - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) @@ -149,8 +122,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "I am not an object") // headers are case-insensitive - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) @@ -181,8 +155,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "milk,true,sugar,true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) @@ -213,8 +188,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "milk,true,sugar,true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) @@ -245,8 +221,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "milk,123,sugar,true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -277,8 +254,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "milk,123,sugar,true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -309,8 +287,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "milk=123,sugar=true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -341,8 +320,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "milk=true,sugar=true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -370,8 +350,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "1,2,3,4,5") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -398,8 +379,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "1,2,3,4,5") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -426,8 +408,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "true,false,true,false,true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -454,8 +437,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "true,false,true,false,true") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 5) @@ -482,8 +466,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "1,false,2,true,5,false") // default encoding. - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 3) @@ -509,8 +494,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "glass") - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -536,8 +522,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "microwave") // this is not a cup! - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -565,8 +552,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "2") - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -592,41 +580,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) request.Header.Set("coffeecups", "1200") // that's a lot of cups dude, we only have one dishwasher. - - valid, errors := v.ValidateHeaderParams(request) - - assert.False(t, valid) - assert.Len(t, errors, 1) - assert.Equal(t, "Instead of '1200', "+ - "use one of the allowed values: '1, 2, 99'", errors[0].HowToFix) -} - -func TestNewValidator_HeaderParamSetPath(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /vending/drinks: - get: - parameters: - - name: coffeeCups - in: header - required: true - schema: - type: integer - enum: [1,2,99]` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - m, _ := doc.BuildV3Model() - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodGet, "https://things.com/vending/drinks", nil) - request.Header.Set("coffeecups", "1200") // that's a lot of cups dude, we only have one dishwasher. - - // preset the path - path, _, pv := paths.FindPath(request, &m.Model) - v.SetPathItem(path, pv) - - valid, errors := v.ValidateHeaderParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHeaderParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) diff --git a/parameters/parameters.go b/parameters/parameters.go index 2f49f82..820b69a 100644 --- a/parameters/parameters.go +++ b/parameters/parameters.go @@ -20,36 +20,26 @@ import ( // Each method accepts an *http.Request and returns true if validation passed, // false if validation failed and a slice of ValidationError pointers. type ParameterValidator interface { - - // SetPathItem will set the pathItem for the ParameterValidator, all validations will be performed against this pathItem - // otherwise if not set, each validation will perform a lookup for the pathItem based on the *http.Request - SetPathItem(path *v3.PathItem, pathValue string) - // ValidateQueryParams accepts an *http.Request and validates the query parameters against the OpenAPI specification. // The method will locate the correct path, and operation, based on the verb. The parameters for the operation // will be matched and validated against what has been supplied in the http.Request query string. - ValidateQueryParams(request *http.Request) (bool, []*errors.ValidationError) + ValidateQueryParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) // ValidateHeaderParams validates the header parameters contained within *http.Request. It returns a boolean // stating true if validation passed (false for failed), and a slice of errors if validation failed. - ValidateHeaderParams(request *http.Request) (bool, []*errors.ValidationError) + ValidateHeaderParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) // ValidateCookieParams validates the cookie parameters contained within *http.Request. // It returns a boolean stating true if validation passed (false for failed), and a slice of errors if validation failed. - ValidateCookieParams(request *http.Request) (bool, []*errors.ValidationError) + ValidateCookieParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) // ValidatePathParams validates the path parameters contained within *http.Request. It returns a boolean stating true // if validation passed (false for failed), and a slice of errors if validation failed. - ValidatePathParams(request *http.Request) (bool, []*errors.ValidationError) + ValidatePathParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) // ValidateSecurity validates the security requirements for the operation. It returns a boolean stating true // if validation passed (false for failed), and a slice of errors if validation failed. - ValidateSecurity(request *http.Request) (bool, []*errors.ValidationError) -} - -func (v *paramValidator) SetPathItem(path *v3.PathItem, pathValue string) { - v.pathItem = path - v.pathValue = pathValue + ValidateSecurity(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) } // NewParameterValidator will create a new ParameterValidator from an OpenAPI 3+ document @@ -58,8 +48,6 @@ func NewParameterValidator(document *v3.Document) ParameterValidator { } type paramValidator struct { - document *v3.Document - pathItem *v3.PathItem - pathValue string - errors []*errors.ValidationError + document *v3.Document + errors []*errors.ValidationError } diff --git a/parameters/path_parameters.go b/parameters/path_parameters.go index 00726ca..a6afa0f 100644 --- a/parameters/path_parameters.go +++ b/parameters/path_parameters.go @@ -16,26 +16,10 @@ import ( v3 "github.com/pb33f/libopenapi/datamodel/high/v3" ) -func (v *paramValidator) ValidatePathParams(request *http.Request) (bool, []*errors.ValidationError) { - - // find path - var pathItem *v3.PathItem - var errs []*errors.ValidationError - var foundPath string - if v.pathItem == nil && v.pathValue == "" { - pathItem, errs, foundPath = paths.FindPath(request, v.document) - if pathItem == nil || errs != nil { - v.errors = errs - return false, errs - } - } else { - pathItem = v.pathItem - foundPath = v.pathValue - } - +func (v *paramValidator) ValidatePathParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { // split the path into segments submittedSegments := strings.Split(paths.StripRequestPath(request, v.document), helpers.Slash) - pathSegments := strings.Split(foundPath, helpers.Slash) + pathSegments := strings.Split(pathValue, helpers.Slash) // extract params for the operation var params = helpers.ExtractParamsForOperation(request, pathItem) @@ -283,7 +267,7 @@ func (v *paramValidator) ValidatePathParams(request *http.Request) (bool, []*err } } - errors.PopulateValidationErrors(validationErrors, request, foundPath) + errors.PopulateValidationErrors(validationErrors, request, pathValue) if len(validationErrors) > 0 { return false, validationErrors diff --git a/parameters/path_parameters_test.go b/parameters/path_parameters_test.go index 3ff659d..5985952 100644 --- a/parameters/path_parameters_test.go +++ b/parameters/path_parameters_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/pb33f/libopenapi" - "github.com/pb33f/libopenapi-validator/paths" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/pb33f/libopenapi-validator/paths" ) func TestNewValidator_SimpleArrayEncodedPath(t *testing.T) { @@ -35,7 +35,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodPatch, "https://things.com/burgers/1,2,3,4,5/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -63,7 +65,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/1,pizza,3,4,false/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -95,7 +99,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/1,true,0,frogs,false/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 3) @@ -127,7 +133,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/id,1234,vegetarian,true/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -158,7 +166,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/id,hello,vegetarian,there/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -191,7 +201,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/id=1234,vegetarian=true/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -223,7 +235,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/id=toast,vegetarian=chicken/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -255,7 +269,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/id,1234,vegetarian,true/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -281,7 +297,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/hello/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -309,7 +327,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/1/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -339,7 +359,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/14/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -365,7 +387,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/hello/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -393,7 +417,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.hello/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -422,7 +448,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.3/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -452,7 +480,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.hello/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -482,7 +512,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.3,4,5,6/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -512,7 +544,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.3.4.5.6/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -542,7 +576,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.3.Not a number.5.6/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -572,7 +608,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.3,4,Not a number,6/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -605,7 +643,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.id,hello,vegetarian,why/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -639,7 +679,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.id=hello.vegetarian=why/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -678,7 +720,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.id=1234.vegetarian=true/locate/bigMac", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -716,7 +760,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.id=1234.vegetarian=true/locate/bigMac", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -743,7 +789,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burgerId=5/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -770,7 +818,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burgerId=I am not a number/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -799,7 +849,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burgerId=3/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -829,7 +881,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burgerId=false/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -856,7 +910,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burgerId=I am also not a bool/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -890,7 +946,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burger=id,1234,vegetarian,false/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -923,7 +981,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burger=id,1234,vegetarian,I am not a bool/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -956,7 +1016,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;id=1234;vegetarian=false/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -989,7 +1051,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;id=1234;vegetarian=I am not a boolean/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1018,7 +1082,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burger=1,2,3,4,5/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1047,7 +1113,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burger=1,2,not a number,4,false/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -1076,7 +1144,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burger=1;burger=2;burger=3/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1105,42 +1175,15 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burger=1;burger=I am not an int;burger=3/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "Path array parameter 'burger' is not a valid number", errors[0].Message) } -func TestNewValidator_PathParams_PathNotFound(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /burgers/{;burger*}/locate: - parameters: - - name: burger - in: path - style: matrix - explode: true - schema: - type: array - items: - type: integer - get: - operationId: locateBurgers` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodGet, "https://things.com/I do not exist", nil) - valid, errors := v.ValidatePathParams(request) - - assert.False(t, valid) - assert.Len(t, errors, 1) -} - func TestNewValidator_PathParamStringEnumValid(t *testing.T) { spec := `openapi: 3.1.0 @@ -1162,7 +1205,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/bigMac/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1190,7 +1235,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/hello/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1220,7 +1267,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/big/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1250,7 +1299,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/2/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1277,7 +1328,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/3284/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1306,7 +1359,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.2/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1334,7 +1389,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/.22334/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1364,42 +1421,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burgerId=22334/locate", nil) - valid, errors := v.ValidatePathParams(request) - - assert.False(t, valid) - assert.Len(t, errors, 1) - assert.Equal(t, "Path parameter 'burgerId' does not match allowed values", errors[0].Message) - assert.Equal(t, "Instead of '22334', use one of the allowed values: '1, 2, 99, 100'", errors[0].HowToFix) -} - -func TestNewValidator_SetPathForPathParam(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /burgers/{;burgerId}/locate: - parameters: - - name: burgerId - in: path - style: matrix - schema: - type: number - enum: [1,2,99,100] - get: - operationId: locateBurgers` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/;burgerId=22334/locate", nil) - - // preset the path - path, _, pv := paths.FindPath(request, &m.Model) - v.SetPathItem(path, pv) - - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1431,7 +1455,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/lorem/ipsum/burgers/d6d8d513-686c-466f-9f5a-1c051b6b4f3f/locate", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1459,7 +1485,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/", nil) - valid, errors := v.ValidatePathParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidatePathParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) diff --git a/parameters/query_parameters.go b/parameters/query_parameters.go index b611763..00f1cf2 100644 --- a/parameters/query_parameters.go +++ b/parameters/query_parameters.go @@ -13,28 +13,12 @@ import ( "github.com/pb33f/libopenapi-validator/errors" "github.com/pb33f/libopenapi-validator/helpers" - "github.com/pb33f/libopenapi-validator/paths" "github.com/pb33f/libopenapi/datamodel/high/base" v3 "github.com/pb33f/libopenapi/datamodel/high/v3" "github.com/pb33f/libopenapi/orderedmap" ) -func (v *paramValidator) ValidateQueryParams(request *http.Request) (bool, []*errors.ValidationError) { - // find path - var pathItem *v3.PathItem - var foundPath string - var errs []*errors.ValidationError - if v.pathItem == nil { - pathItem, errs, foundPath = paths.FindPath(request, v.document) - if pathItem == nil || errs != nil { - v.errors = errs - return false, errs - } - } else { - pathItem = v.pathItem - foundPath = v.pathValue - } - +func (v *paramValidator) ValidateQueryParams(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { // extract params for the operation params := helpers.ExtractParamsForOperation(request, pathItem) queryParams := make(map[string][]*helpers.QueryParam) @@ -213,7 +197,7 @@ doneLooking: } } - errors.PopulateValidationErrors(validationErrors, request, foundPath) + errors.PopulateValidationErrors(validationErrors, request, pathValue) v.errors = validationErrors if len(validationErrors) > 0 { diff --git a/parameters/query_parameters_test.go b/parameters/query_parameters_test.go index 648ca2b..fc324e5 100644 --- a/parameters/query_parameters_test.go +++ b/parameters/query_parameters_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/pb33f/libopenapi" - "github.com/pb33f/libopenapi-validator/paths" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/pb33f/libopenapi-validator/paths" ) func TestNewValidator_QueryParamMissing(t *testing.T) { @@ -35,7 +35,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) assert.Equal(t, "Query parameter 'fishy' is missing", errors[0].Message) @@ -66,7 +68,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -96,7 +100,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) assert.Equal(t, "Query parameter 'fishy' failed to validate", errors[0].Message) @@ -126,7 +132,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=salmon", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -154,7 +162,9 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -182,7 +192,9 @@ paths: request, _ := http.NewRequest(http.MethodPut, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -210,7 +222,9 @@ paths: request, _ := http.NewRequest(http.MethodDelete, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -238,7 +252,9 @@ paths: request, _ := http.NewRequest(http.MethodOptions, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -266,7 +282,9 @@ paths: request, _ := http.NewRequest(http.MethodHead, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -294,7 +312,9 @@ paths: request, _ := http.NewRequest(http.MethodPatch, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -322,39 +342,14 @@ paths: request, _ := http.NewRequest(http.MethodTrace, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) } -func TestNewValidator_QueryParamBadPath(t *testing.T) { - spec := `openapi: 3.1.0 -paths: - /a/fishy/on/a/dishy: - get: - parameters: - - name: fishy - in: query - required: true - schema: - type: number - operationId: locateFishy -` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodGet, "https://things.com/Not/Found/dishy?fishy=cod", nil) - - valid, errors := v.ValidateQueryParams(request) - assert.False(t, valid) - assert.NotNil(t, errors) -} - func TestNewValidator_QueryParamWrongTypeNumber(t *testing.T) { spec := `openapi: 3.1.0 paths: @@ -377,7 +372,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.NotNil(t, errors) @@ -406,7 +403,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=123", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -436,7 +435,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=300", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -466,7 +467,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=123", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) assert.Equal(t, "Query parameter 'fishy' failed to validate", errors[0].Message) @@ -494,7 +497,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=123.223", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -522,7 +527,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.NotNil(t, errors) @@ -551,7 +558,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=true", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) @@ -578,7 +587,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod&fishy=haddock", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "Query parameter 'fishy' does not match allowed values", errors[0].Message) @@ -606,7 +617,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=22", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "Query parameter 'fishy' does not match allowed values", errors[0].Message) @@ -634,7 +647,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -663,7 +678,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod&fishy=haddock", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Nil(t, errors) } @@ -693,7 +710,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod&fishy=haddock", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "Instead of 'haddock', use one of the allowed values: 'cod, halibut'", errors[0].HowToFix) @@ -724,7 +743,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1&fishy=2", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "Instead of '2', use one of the allowed values: '1, 99'", errors[0].HowToFix) @@ -754,7 +775,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod&fishy=haddock", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -791,7 +814,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod,haddock", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -820,7 +845,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1&fishy=2", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -849,7 +876,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=haddock&fishy=cod", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -879,7 +908,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod,haddock,mackrel", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -908,7 +939,9 @@ operationId: locateFishy` request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod&fishy=haddock", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -942,7 +975,9 @@ operationId: locateFishy` v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=12&fishy=12.12&fishy=1234567789.1233456657", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -970,7 +1005,9 @@ operationId: locateFishy` v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=12|12345.2344|22111233444.342452435", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -1002,7 +1039,9 @@ operationId: locateFishy` v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=ocean|12|silver|12.2345", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -1038,7 +1077,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"cod\":\"cakes\"}&fishy={\"crab\":\"legs\"}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -1079,7 +1120,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"vinegar\":\"cakes\",\"chips\":\"hello\"}&fishy={\"vinegar\":true,\"chips\":123.223}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Len(t, errors[0].SchemaValidationErrors, 2) @@ -1115,7 +1158,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"vinegar\":true,\"chips\":12}&fishy={\"vinegar\":true,\"chips\":123.333}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -1160,7 +1205,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"vinegar\":\"cakes\",\"chips\":\"hello\"}&fishy={\"vinegar\":true,\"chips\":123}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Len(t, errors[0].SchemaValidationErrors, 2) @@ -1206,7 +1253,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"vinegar\":false,\"chips\":999}&fishy={\"vinegar\":true,\"chips\":123}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -1243,7 +1292,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?vinegar=true&chips=12", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -1280,7 +1331,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?vinegar=true&chips=false", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "expected number, but got boolean", errors[0].SchemaValidationErrors[0].Reason) @@ -1321,7 +1374,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"vinegar\":false,\"chips\":999}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1361,7 +1416,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"vinegar\":false,\"chips\":\"I am invalid\"}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "expected number, but got string", errors[0].SchemaValidationErrors[0].Reason) @@ -1401,7 +1458,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=I am not json", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "Query parameter 'fishy' is not valid JSON", errors[0].Message) @@ -1442,7 +1501,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy={\"vinegar\":1234,\"chips\":false}", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Len(t, errors[0].SchemaValidationErrors, 2) @@ -1473,7 +1534,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=$$oh", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1505,7 +1568,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod,haddock,mackrel", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1537,7 +1602,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod,haddock,mackrel", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1569,7 +1636,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod&fishy=haddock&fishy=mackrel", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1601,7 +1670,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod|haddock|mackrel", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -1631,7 +1702,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod|haddock|mackrel&fishy=breaded|cooked|fried", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) } @@ -1670,7 +1743,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod|haddock|mackrel&plate=flat|round", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1715,7 +1790,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=fish|salmon|dish|stew", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1760,7 +1837,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=fish|salmon|dish|cakes", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1806,7 +1885,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=fish%20salmon%20dish%20stew", nil) // dumb, don't do this. - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1850,7 +1931,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=fish%20salmon%20dish%20coffee", nil) // dumb, don't do this. - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "value must be one of \"salad\", \"soup\", \"stew\"", errors[0].SchemaValidationErrors[0].Reason) @@ -1889,7 +1972,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=cod|haddock|mackrel&plate=flat,round", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -1918,7 +2003,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy[ocean]=atlantic&fishy[salt]=12", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1948,7 +2035,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=atlantic&fishy=12", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 2) @@ -1994,7 +2083,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=ocean,atlantic,fins,4&dishy=hot,true,salty,true", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -2038,7 +2129,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=ocean,atlantic,fins,4&dishy=hot,no,salty,why", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -2078,7 +2171,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1,2,3&dishy=a,little,plate", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -2118,7 +2213,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1&fishy=2&fishy=3&dishy=a&dishy=little&dishy=dish", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -2157,7 +2254,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1,2,3&dishy=little,dishy", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 4) assert.Equal(t, "The query parameter 'fishy' has a default or 'form' encoding defined, however the "+ @@ -2198,7 +2297,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1|2|3&dishy=little|dishy", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -2236,7 +2337,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1%202%203&dishy=little%20dishy", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -2274,7 +2377,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=1|%202%203&dishy=little%20dishy", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "Convert the value '1|' into a number", errors[0].HowToFix) @@ -2309,47 +2414,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy[ocean]=atlantic&fishy[salt]=12", nil) - valid, errors := v.ValidateQueryParams(request) - assert.False(t, valid) - - assert.Len(t, errors, 1) - assert.Equal(t, "expected boolean, but got number", errors[0].SchemaValidationErrors[0].Reason) -} - -func TestNewValidator_QueryParamSetPath(t *testing.T) { - spec := `openapi: 3.1.0 -paths: - /a/fishy/on/a/dishy: - get: - parameters: - - name: fishy - in: query - required: true - style: deepObject - schema: - type: object - properties: - ocean: - type: string - salt: - type: boolean - required: [ocean, salt] - operationId: locateFishy` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodGet, - "https://things.com/a/fishy/on/a/dishy?fishy[ocean]=atlantic&fishy[salt]=12", nil) - - // preset the path - path, _, pv := paths.FindPath(request, &m.Model) - v.SetPathItem(path, pv) - - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -2418,7 +2485,9 @@ paths: "&dishy[size]=big&dishy[numCracks]=false"+ "&cake[message]=happy%20birthday&cake[numCandles]=false", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 3) @@ -2498,7 +2567,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/anything/queryParams/deepObject/map?mapArrParam[test2]=test3&mapArrParam[test2]=test4&mapArrParam[test]=test&mapArrParam[test]=test2", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -2533,7 +2604,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/anything/queryParams/deepObject/map?mapArrParam[test2]=test3&mapArrParam[test2]=test4&mapArrParam[test]=test&mapArrParam[test]=test2", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -2570,7 +2643,9 @@ paths: request, _ := http.NewRequest(http.MethodGet, "https://things.com/anything/queryParams/deepObject/map?mapArrParam[test2]=23&mapArrParam[test2]=test4&mapArrParam[test]=test&mapArrParam[test]=test2", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "expected string, but got number", errors[0].SchemaValidationErrors[0].Reason) @@ -2616,7 +2691,9 @@ components: request, _ := http.NewRequest(http.MethodGet, "http://localhost:9090/anything/queryParams/deepObject/obj?objParam=blahdedahdedah", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } @@ -2664,7 +2741,9 @@ components: request, _ := http.NewRequest(http.MethodGet, "http://localhost:9090/anything/queryParams/deepObject/obj?objParam=blahdedahdedah", nil) - valid, errors := v.ValidateQueryParams(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateQueryParams(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) assert.Equal(t, "The query parameter 'objParam' is defined as an object,"+ diff --git a/parameters/validate_security.go b/parameters/validate_security.go index e5e7ee5..73e8845 100644 --- a/parameters/validate_security.go +++ b/parameters/validate_security.go @@ -10,27 +10,11 @@ import ( "github.com/pb33f/libopenapi-validator/errors" "github.com/pb33f/libopenapi-validator/helpers" - "github.com/pb33f/libopenapi-validator/paths" v3 "github.com/pb33f/libopenapi/datamodel/high/v3" "github.com/pb33f/libopenapi/orderedmap" ) -func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*errors.ValidationError) { - // find path - var pathItem *v3.PathItem - var pathFound string - var errs []*errors.ValidationError - if v.pathItem == nil { - pathItem, errs, pathFound = paths.FindPath(request, v.document) - if pathItem == nil || errs != nil { - v.errors = errs - return false, errs - } - } else { - pathItem = v.pathItem - pathFound = v.pathValue - } - +func (v *paramValidator) ValidateSecurity(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { // extract security for the operation security := helpers.ExtractSecurityForOperation(request, pathItem) @@ -55,7 +39,7 @@ func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*error HowToFix: "Add the missing security scheme to the components", }, } - errors.PopulateValidationErrors(validationErrors, request, pathFound) + errors.PopulateValidationErrors(validationErrors, request, pathValue) return false, validationErrors } @@ -78,7 +62,7 @@ func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*error }, } - errors.PopulateValidationErrors(validationErrors, request, pathFound) + errors.PopulateValidationErrors(validationErrors, request, pathValue) return false, validationErrors } @@ -100,7 +84,7 @@ func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*error }, } - errors.PopulateValidationErrors(validationErrors, request, pathFound) + errors.PopulateValidationErrors(validationErrors, request, pathValue) return false, validationErrors } @@ -126,7 +110,7 @@ func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*error }, } - errors.PopulateValidationErrors(validationErrors, request, pathFound) + errors.PopulateValidationErrors(validationErrors, request, pathValue) return false, validationErrors } @@ -153,7 +137,7 @@ func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*error }, } - errors.PopulateValidationErrors(validationErrors, request, pathFound) + errors.PopulateValidationErrors(validationErrors, request, pathValue) return false, validationErrors } diff --git a/parameters/validate_security_test.go b/parameters/validate_security_test.go index 823b20d..ffb91e4 100644 --- a/parameters/validate_security_test.go +++ b/parameters/validate_security_test.go @@ -5,10 +5,10 @@ package parameters import ( "github.com/pb33f/libopenapi" - "github.com/pb33f/libopenapi-validator/paths" "github.com/stretchr/testify/assert" "net/http" "testing" + "github.com/pb33f/libopenapi-validator/paths" ) func TestParamValidator_ValidateSecurity_APIKeyHeader_NotFound(t *testing.T) { @@ -36,7 +36,9 @@ components: request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) assert.Equal(t, "API Key X-API-Key not found in header", errors[0].Message) @@ -71,7 +73,9 @@ components: request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) request.Header.Add("X-API-Key", "1234") - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.True(t, valid) assert.Equal(t, 0, len(errors)) } @@ -101,7 +105,9 @@ components: request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) assert.Equal(t, "API Key X-API-Key not found in query", errors[0].Message) @@ -138,7 +144,9 @@ components: request, _ := http.NewRequest(http.MethodPost, "https://things.com/products?X-API-Key=12345", nil) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.True(t, valid) assert.Equal(t, 0, len(errors)) } @@ -168,7 +176,9 @@ components: request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) assert.Equal(t, "API Key X-API-Key not found in cookies", errors[0].Message) @@ -207,7 +217,9 @@ components: Value: "1234", }) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.True(t, valid) assert.Equal(t, 0, len(errors)) } @@ -236,7 +248,9 @@ components: request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) assert.Equal(t, "Authorization header for 'basic' scheme", errors[0].Message) @@ -270,42 +284,13 @@ components: request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) request.Header.Add("Authorization", "Basic 1234") - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.True(t, valid) assert.Equal(t, 0, len(errors)) } -func TestParamValidator_ValidateSecurity_BadPath(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /products: - post: - security: - - ApiKeyAuth: - - write:products -components: - securitySchemes: - ApiKeyAuth: - type: http - scheme: basic -` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodPost, "https://things.com/blimpo", nil) - valid, errors := v.ValidateSecurity(request) - assert.False(t, valid) - assert.Equal(t, 1, len(errors)) - assert.Equal(t, request.Method, errors[0].RequestMethod) - assert.Equal(t, request.URL.Path, errors[0].RequestPath) - assert.Equal(t, "", errors[0].SpecPath) -} - func TestParamValidator_ValidateSecurity_MissingSecuritySchemes(t *testing.T) { spec := `openapi: 3.1.0 @@ -325,7 +310,9 @@ components: {} v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) } @@ -348,31 +335,9 @@ paths: v := NewParameterValidator(&m.Model) request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) - valid, errors := v.ValidateSecurity(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateSecurity(request, pathItem, pathValue) assert.False(t, valid) assert.Equal(t, 1, len(errors)) } - -func TestParamValidator_ValidateSecurity_PresetPath(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /products: - post: -` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - - v := NewParameterValidator(&m.Model) - - request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil) - pathItem, errs, _ := paths.FindPath(request, &m.Model) - assert.Nil(t, errs) - v.(*paramValidator).pathItem = pathItem - - valid, errors := v.ValidateSecurity(request) - assert.True(t, valid) - assert.Equal(t, 0, len(errors)) -} diff --git a/requests/request_body.go b/requests/request_body.go index cf95ef9..3a73e59 100644 --- a/requests/request_body.go +++ b/requests/request_body.go @@ -20,12 +20,7 @@ type RequestBodyValidator interface { // ValidateRequestBody will validate the request body for an operation. The first return value will be true if the // request body is valid, false if it is not. The second return value will be a slice of ValidationError pointers if // the body is not valid. - ValidateRequestBody(request *http.Request) (bool, []*errors.ValidationError) - - // SetPathItem will set the pathItem for the RequestBodyValidator, all validations will be performed - // against this pathItem otherwise if not set, each validation will perform a lookup for the pathItem - // based on the *http.Request - SetPathItem(path *v3.PathItem, pathValue string) + ValidateRequestBody(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) } // NewRequestBodyValidator will create a new RequestBodyValidator from an OpenAPI 3+ document @@ -33,10 +28,6 @@ func NewRequestBodyValidator(document *v3.Document) RequestBodyValidator { return &requestBodyValidator{document: document, schemaCache: &sync.Map{}} } -func (v *requestBodyValidator) SetPathItem(path *v3.PathItem, pathValue string) { - v.pathItem = path - v.pathValue = pathValue -} type schemaCache struct { schema *base.Schema @@ -46,8 +37,6 @@ type schemaCache struct { type requestBodyValidator struct { document *v3.Document - pathItem *v3.PathItem - pathValue string errors []*errors.ValidationError schemaCache *sync.Map } diff --git a/requests/validate_body.go b/requests/validate_body.go index c13294d..a67c110 100644 --- a/requests/validate_body.go +++ b/requests/validate_body.go @@ -9,29 +9,15 @@ import ( "github.com/pb33f/libopenapi-validator/errors" "github.com/pb33f/libopenapi-validator/helpers" - "github.com/pb33f/libopenapi-validator/paths" "github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/utils" + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" ) -func (v *requestBodyValidator) ValidateRequestBody(request *http.Request) (bool, []*errors.ValidationError) { - // find path - var pathItem = v.pathItem - var foundPath string - if v.pathItem == nil { - var validationErrors []*errors.ValidationError - pathItem, validationErrors, foundPath = paths.FindPath(request, v.document) - if pathItem == nil || validationErrors != nil { - v.errors = validationErrors - return false, validationErrors - } - } else { - foundPath = v.pathValue - } - +func (v *requestBodyValidator) ValidateRequestBody(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { operation := helpers.ExtractOperation(request, pathItem) if operation == nil { - return false, []*errors.ValidationError{errors.OperationNotFound(pathItem, request, request.Method, foundPath)} + return false, []*errors.ValidationError{errors.OperationNotFound(pathItem, request, request.Method, pathValue)} } if operation.RequestBody == nil { return true, nil @@ -48,14 +34,14 @@ func (v *requestBodyValidator) ValidateRequestBody(request *http.Request) (bool, // request body is not required, the validation stop there. return true, nil } - return false, []*errors.ValidationError{errors.RequestContentTypeNotFound(operation, request, foundPath)} + return false, []*errors.ValidationError{errors.RequestContentTypeNotFound(operation, request, pathValue)} } // extract the media type from the content type header. ct, _, _ := helpers.ExtractContentType(contentType) mediaType, ok := operation.RequestBody.Content.Get(ct) if !ok { - return false, []*errors.ValidationError{errors.RequestContentTypeNotFound(operation, request, foundPath)} + return false, []*errors.ValidationError{errors.RequestContentTypeNotFound(operation, request, pathValue)} } // we currently only support JSON validation for request bodies @@ -100,7 +86,7 @@ func (v *requestBodyValidator) ValidateRequestBody(request *http.Request) (bool, // render the schema, to be used for validation validationSucceeded, validationErrors := ValidateRequestSchema(request, schema, renderedInline, renderedJSON) - errors.PopulateValidationErrors(validationErrors, request, foundPath) + errors.PopulateValidationErrors(validationErrors, request, pathValue) return validationSucceeded, validationErrors } diff --git a/requests/validate_body_test.go b/requests/validate_body_test.go index 4bc8717..cb96c23 100644 --- a/requests/validate_body_test.go +++ b/requests/validate_body_test.go @@ -41,7 +41,9 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger", http.NoBody) - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -84,7 +86,9 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "thomas/tank-engine") // wtf kinda content type is this? - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -131,108 +135,14 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/yaml") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) } -func TestValidateBody_PathNotFound(t *testing.T) { - spec := `openapi: 3.1.0 -paths: - /burgers/createBurger: - post: - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - patties: - type: integer - vegetarian: - type: boolean` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - v := NewRequestBodyValidator(&m.Model) - - // mix up the primitives to fire two schema violations. - body := map[string]interface{}{ - "name": "Big Mac", - "patties": false, - "vegetarian": 2, - } - - bodyBytes, _ := json.Marshal(body) - - request, _ := http.NewRequest(http.MethodPost, "https://things.com/I do not exist", - bytes.NewBuffer(bodyBytes)) - request.Header.Set("Content-Type", "application/json") - - valid, errors := v.ValidateRequestBody(request) - - assert.False(t, valid) - assert.Len(t, errors, 1) - assert.Equal(t, "POST Path '/I do not exist' not found", errors[0].Message) - assert.Equal(t, request.Method, errors[0].RequestMethod) - assert.Equal(t, request.URL.Path, errors[0].RequestPath) - assert.Equal(t, "", errors[0].SpecPath) -} - -func TestValidateBody_OperationNotFound(t *testing.T) { - spec := `openapi: 3.1.0 -paths: - /burgers/createBurger: - post: - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - patties: - type: integer - vegetarian: - type: boolean` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - m, _ := doc.BuildV3Model() - v := NewRequestBodyValidator(&m.Model) - - // mix up the primitives to fire two schema violations. - body := map[string]interface{}{ - "name": "Big Mac", - "patties": false, - "vegetarian": 2, - } - - bodyBytes, _ := json.Marshal(body) - - request, _ := http.NewRequest(http.MethodGet, "https://things.com/burgers/createBurger", - bytes.NewBuffer(bodyBytes)) - request.Header.Set("Content-Type", "application/json") - - pathItem := m.Model.Paths.PathItems.First().Value() - pathValue := m.Model.Paths.PathItems.First().Key() - v.SetPathItem(pathItem, pathValue) - - valid, errors := v.ValidateRequestBody(request) - - assert.False(t, valid) - assert.Len(t, errors, 1) - assert.Equal(t, "GET operation request content type 'GET' does not exist", errors[0].Message) - assert.Equal(t, request.Method, errors[0].RequestMethod) - assert.Equal(t, request.URL.Path, errors[0].RequestPath) - assert.Equal(t, "/burgers/createBurger", errors[0].SpecPath) -} - func TestValidateBody_SetPath(t *testing.T) { spec := `openapi: 3.1.0 paths: @@ -269,11 +179,9 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - // preset the path - path, _, pv := paths.FindPath(request, &m.Model) - v.SetPathItem(path, pv) - - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -317,11 +225,9 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("content-type", "application/not-json") - // preset the path - path, _, pv := paths.FindPath(request, &m.Model) - v.SetPathItem(path, pv) - - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -363,11 +269,9 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger", bytes.NewBuffer(bodyBytes)) - // preset the path - path, _, pv := paths.FindPath(request, &m.Model) - v.SetPathItem(path, pv) - - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -411,10 +315,12 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) // double-tap to hit the cache - _, _ = v.ValidateRequestBody(request) + _, _ = v.ValidateRequestBody(request, nil, "") assert.False(t, valid) assert.Len(t, errors, 1) @@ -460,10 +366,12 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) // double-tap to hit the cache - _, _ = v.ValidateRequestBody(request) + _, _ = v.ValidateRequestBody(request, nil, "") assert.False(t, valid) assert.Len(t, errors, 1) @@ -507,7 +415,9 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -549,7 +459,9 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json; charset=utf-8; boundary=12345") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -616,7 +528,9 @@ components: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -682,7 +596,9 @@ components: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -770,7 +686,9 @@ components: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -855,7 +773,9 @@ components: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -911,7 +831,9 @@ components: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -968,7 +890,9 @@ components: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -994,7 +918,9 @@ paths: http.NoBody) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1019,7 +945,9 @@ paths: http.NoBody) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1063,7 +991,9 @@ components: http.NoBody) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1085,7 +1015,9 @@ paths: http.NoBody) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -1141,10 +1073,12 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) // double-tap to hit the cache - _, _ = v.ValidateRequestBody(request) + _, _ = v.ValidateRequestBody(request, nil, "") assert.False(t, valid) assert.Len(t, errors, 1) @@ -1187,7 +1121,9 @@ components: bytes.NewBuffer([]byte("{\"bad\": \"json\",}"))) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateRequestBody(request) + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateRequestBody(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -1324,7 +1260,7 @@ func TestValidateBody_SchemaNoType_Issue75(t *testing.T) { } reqBodyValidator := NewRequestBodyValidator(&v3Model.Model) - isSuccess, valErrs := reqBodyValidator.ValidateRequestBody(req) + isSuccess, valErrs := reqBodyValidator.ValidateRequestBody(req, nil, "") assert.False(t, isSuccess) assert.Len(t, valErrs, 1) diff --git a/validator.go b/validator.go index 2119cfc..045a433 100644 --- a/validator.go +++ b/validator.go @@ -27,10 +27,10 @@ type Validator interface { // ValidateHttpRequest will validate an *http.Request object against an OpenAPI 3+ document. // The path, query, cookie and header parameters and request body are validated. - ValidateHttpRequest(request *http.Request) (bool, []*errors.ValidationError) + ValidateHttpRequest(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) // ValidateHttpRequestSync will validate an *http.Request object against an OpenAPI 3+ document syncronously and without spawning any goroutines. // The path, query, cookie and header parameters and request body are validated. - ValidateHttpRequestSync(request *http.Request) (bool, []*errors.ValidationError) + ValidateHttpRequestSync(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) // ValidateHttpResponse will an *http.Response object against an OpenAPI 3+ document. // The response body is validated. The request is only used to extract the correct reponse from the spec. @@ -110,8 +110,6 @@ func (v *validator) ValidateHttpResponse( v.errors = errs return false, errs } - v.foundPath = pathItem - v.foundPathValue = pathValue responseBodyValidator := v.responseValidator responseBodyValidator.SetPathItem(pathItem, pathValue) @@ -122,8 +120,6 @@ func (v *validator) ValidateHttpResponse( if len(responseErrors) > 0 { return false, responseErrors } - v.foundPath = nil - v.foundPathValue = "" return true, nil } @@ -140,50 +136,26 @@ func (v *validator) ValidateHttpRequestResponse( v.errors = errs return false, errs } - v.foundPath = pathItem - v.foundPathValue = pathValue responseBodyValidator := v.responseValidator responseBodyValidator.SetPathItem(pathItem, pathValue) // validate request and response - _, requestErrors := v.ValidateHttpRequest(request) + _, requestErrors := v.ValidateHttpRequest(request, pathItem, pathValue) _, responseErrors := responseBodyValidator.ValidateResponseBody(request, response) if len(requestErrors) > 0 || len(responseErrors) > 0 { return false, append(requestErrors, responseErrors...) } - v.foundPath = nil - v.foundPathValue = "" return true, nil } -func (v *validator) ValidateHttpRequest(request *http.Request) (bool, []*errors.ValidationError) { - - // find path - var pathItem *v3.PathItem - var pathValue string - var errs []*errors.ValidationError - if v.foundPath == nil { - pathItem, errs, pathValue = paths.FindPath(request, v.v3Model) - if pathItem == nil || errs != nil { - v.errors = errs - return false, errs - } - v.foundPath = pathItem - v.foundPathValue = pathValue - } else { - pathItem = v.foundPath - pathValue = v.foundPathValue - } - +func (v *validator) ValidateHttpRequest(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { // create a new parameter validator paramValidator := v.paramValidator - paramValidator.SetPathItem(pathItem, pathValue) // create a new request body validator reqBodyValidator := v.requestValidator - reqBodyValidator.SetPathItem(pathItem, pathValue) // create some channels to handle async validation doneChan := make(chan bool) @@ -226,7 +198,7 @@ func (v *validator) ValidateHttpRequest(request *http.Request) (bool, []*errors. control chan bool, errorChan chan []*errors.ValidationError, validatorFunc validationFunction) { - valid, pErrs := validatorFunc(request) + valid, pErrs := validatorFunc(request, pathItem, pathValue) if !valid { errorChan <- pErrs } @@ -248,7 +220,7 @@ func (v *validator) ValidateHttpRequest(request *http.Request) (bool, []*errors. } requestBodyValidationFunc := func(control chan bool, errorChan chan []*errors.ValidationError) { - valid, pErrs := reqBodyValidator.ValidateRequestBody(request) + valid, pErrs := reqBodyValidator.ValidateRequestBody(request, pathItem, pathValue) if !valid { errorChan <- pErrs } @@ -273,39 +245,18 @@ func (v *validator) ValidateHttpRequest(request *http.Request) (bool, []*errors. // wait for all the validations to complete <-doneChan - v.foundPathValue = "" - v.foundPath = nil if len(validationErrors) > 0 { return false, validationErrors } return true, nil } -func (v *validator) ValidateHttpRequestSync(request *http.Request) (bool, []*errors.ValidationError) { - // find path - var pathItem *v3.PathItem - var pathValue string - var errs []*errors.ValidationError - if v.foundPath == nil { - pathItem, errs, pathValue = paths.FindPath(request, v.v3Model) - if pathItem == nil || errs != nil { - v.errors = errs - return false, errs - } - v.foundPath = pathItem - v.foundPathValue = pathValue - } else { - pathItem = v.foundPath - pathValue = v.foundPathValue - } - +func (v *validator) ValidateHttpRequestSync(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { // create a new parameter validator paramValidator := v.paramValidator - paramValidator.SetPathItem(pathItem, pathValue) // create a new request body validator reqBodyValidator := v.requestValidator - reqBodyValidator.SetPathItem(pathItem, pathValue) validationErrors := make([]*errors.ValidationError, 0) @@ -317,13 +268,13 @@ func (v *validator) ValidateHttpRequestSync(request *http.Request) (bool, []*err paramValidator.ValidateQueryParams, paramValidator.ValidateSecurity, } { - valid, pErrs := validateFunc(request) + valid, pErrs := validateFunc(request, pathItem, pathValue) if !valid { paramValidationErrors = append(paramValidationErrors, pErrs...) } } - valid, pErrs := reqBodyValidator.ValidateRequestBody(request) + valid, pErrs := reqBodyValidator.ValidateRequestBody(request, pathItem, pathValue) if !valid { paramValidationErrors = append(paramValidationErrors, pErrs...) } @@ -340,8 +291,6 @@ func (v *validator) ValidateHttpRequestSync(request *http.Request) (bool, []*err type validator struct { v3Model *v3.Document document libopenapi.Document - foundPath *v3.PathItem - foundPathValue string paramValidator parameters.ParameterValidator requestValidator requests.RequestBodyValidator responseValidator responses.ResponseBodyValidator @@ -371,5 +320,5 @@ func runValidation(control, doneChan chan bool, } } -type validationFunction func(request *http.Request) (bool, []*errors.ValidationError) +type validationFunction func(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) type validationFunctionAsync func(control chan bool, errorChan chan []*errors.ValidationError) diff --git a/validator_examples_test.go b/validator_examples_test.go index 597ed52..5d4924b 100644 --- a/validator_examples_test.go +++ b/validator_examples_test.go @@ -76,7 +76,7 @@ func ExampleNewValidator_validateHttpRequest() { request, _ := http.NewRequest(http.MethodGet, "/pet/NotAValidPetId", nil) // 5. Validate! - valid, validationErrs := docValidator.ValidateHttpRequest(request) + valid, validationErrs := docValidator.ValidateHttpRequest(request, nil, "") if !valid { for _, e := range validationErrs { diff --git a/validator_test.go b/validator_test.go index d543131..c88033f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -13,9 +13,9 @@ import ( "github.com/pb33f/libopenapi" "github.com/pb33f/libopenapi-validator/helpers" - v3 "github.com/pb33f/libopenapi/datamodel/high/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/pb33f/libopenapi-validator/paths" ) func TestNewValidator(t *testing.T) { @@ -101,50 +101,7 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateHttpRequest(request) - - assert.False(t, valid) - assert.Len(t, errors, 1) - assert.Equal(t, "POST Path '/I am a potato man' not found", errors[0].Message) - -} - -func TestNewValidator_ValidateHttpRequestSync_BadPath(t *testing.T) { - - spec := `openapi: 3.1.0 -paths: - /burgers/createBurger: - post: - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - patties: - type: integer - vegetarian: - type: boolean` - - doc, _ := libopenapi.NewDocument([]byte(spec)) - - v, _ := NewValidator(doc) - - body := map[string]interface{}{ - "name": "Big Mac", - "patties": 2, - "vegetarian": true, - } - - bodyBytes, _ := json.Marshal(body) - - request, _ := http.NewRequest(http.MethodPost, "https://things.com/I am a potato man", - bytes.NewBuffer(bodyBytes)) - request.Header.Set("Content-Type", "application/json") - - valid, errors := v.ValidateHttpRequestSync(request) + valid, errors := v.ValidateHttpRequest(request, nil, "") assert.False(t, valid) assert.Len(t, errors, 1) @@ -187,7 +144,7 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateHttpRequest(request) + valid, errors := v.ValidateHttpRequest(request, nil, "") assert.True(t, valid) assert.Len(t, errors, 0) @@ -228,8 +185,10 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger", bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - - valid, errors := v.ValidateHttpRequestSync(request) + m, _ := doc.BuildV3Model() + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHttpRequestSync(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -258,10 +217,6 @@ paths: doc, _ := libopenapi.NewDocument([]byte(spec)) v, _ := NewValidator(doc) - v.(*validator).foundPath = &v3.PathItem{ - Post: &v3.Operation{}, - } - v.(*validator).foundPathValue = "/burgers/createBurger" body := map[string]interface{}{ "name": "Big Mac", @@ -274,8 +229,10 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger", bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - - valid, errors := v.ValidateHttpRequestSync(request) + m, _ := doc.BuildV3Model() + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHttpRequestSync(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -306,7 +263,7 @@ paths: request, _ := http.NewRequest(http.MethodPatch, "https://things.com/burgers/edd0189c-420b-489c-98f2-0facc5a26f3a/locate", nil) v, _ := NewValidator(doc) - valid, errors := v.ValidateHttpRequest(request) + valid, errors := v.ValidateHttpRequest(request, nil, "") assert.True(t, valid) assert.Len(t, errors, 0) @@ -347,7 +304,7 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateHttpRequest(request) + valid, errors := v.ValidateHttpRequest(request, nil, "") assert.True(t, valid) assert.Len(t, errors, 0) @@ -388,8 +345,10 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger", bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - - valid, errors := v.ValidateHttpRequestSync(request) + m, _ := doc.BuildV3Model() + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHttpRequestSync(request, pathItem, pathValue) assert.True(t, valid) assert.Len(t, errors, 0) @@ -432,7 +391,7 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateHttpRequest(request) + valid, errors := v.ValidateHttpRequest(request, nil, "") assert.False(t, valid) assert.Len(t, errors, 1) @@ -475,8 +434,10 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger", bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - - valid, errors := v.ValidateHttpRequestSync(request) + m, _ := doc.BuildV3Model() + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHttpRequestSync(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1) @@ -525,7 +486,7 @@ paths: bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - valid, errors := v.ValidateHttpRequest(request) + valid, errors := v.ValidateHttpRequest(request, nil, "") assert.False(t, valid) assert.Len(t, errors, 1) @@ -573,8 +534,10 @@ paths: request, _ := http.NewRequest(http.MethodPost, "https://things.com/burgers/createBurger", bytes.NewBuffer(bodyBytes)) request.Header.Set("Content-Type", "application/json") - - valid, errors := v.ValidateHttpRequestSync(request) + m, _ := doc.BuildV3Model() + pathItem, validationErrors, pathValue := paths.FindPath(request, &m.Model) + assert.Len(t, validationErrors, 0) + valid, errors := v.ValidateHttpRequestSync(request, pathItem, pathValue) assert.False(t, valid) assert.Len(t, errors, 1)