Skip to content

Commit ab93afc

Browse files
jacobm-splunkdaveshanley
authored andcommitted
Added request information to all non-schema validation errors
1 parent 0a3478f commit ab93afc

18 files changed

+156
-28
lines changed

errors/error_utilities.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package errors
2+
3+
import (
4+
"net/http"
5+
)
6+
7+
// PopulateValidationErrors mutates the provided validation errors with additional useful error information, that is
8+
// not necessarily available when the ValidationError was created and are standard for all errors.
9+
// Specifically, the RequestPath, SpecPath and RequestMethod are populated.
10+
func PopulateValidationErrors(validationErrors []*ValidationError, request *http.Request, path string) {
11+
for _, validationError := range validationErrors {
12+
validationError.SpecPath = path
13+
validationError.RequestMethod = request.Method
14+
validationError.RequestPath = request.URL.Path
15+
}
16+
}

errors/request_errors.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/pb33f/libopenapi/orderedmap"
1414
)
1515

16-
func RequestContentTypeNotFound(op *v3.Operation, request *http.Request) *ValidationError {
16+
func RequestContentTypeNotFound(op *v3.Operation, request *http.Request, specPath string) *ValidationError {
1717
ct := request.Header.Get(helpers.ContentTypeHeader)
1818
var ctypes []string
1919
for pair := orderedmap.First(op.RequestBody.Content); pair != nil; pair = pair.Next() {
@@ -26,23 +26,29 @@ func RequestContentTypeNotFound(op *v3.Operation, request *http.Request) *Valida
2626
request.Method, ct),
2727
Reason: fmt.Sprintf("The content type '%s' of the %s request submitted has not "+
2828
"been defined, it's an unknown type", ct, request.Method),
29-
SpecLine: op.RequestBody.GoLow().Content.KeyNode.Line,
30-
SpecCol: op.RequestBody.GoLow().Content.KeyNode.Column,
31-
Context: op,
32-
HowToFix: fmt.Sprintf(HowToFixInvalidContentType, orderedmap.Len(op.RequestBody.Content), strings.Join(ctypes, ", ")),
29+
SpecLine: op.RequestBody.GoLow().Content.KeyNode.Line,
30+
SpecCol: op.RequestBody.GoLow().Content.KeyNode.Column,
31+
Context: op,
32+
HowToFix: fmt.Sprintf(HowToFixInvalidContentType, orderedmap.Len(op.RequestBody.Content), strings.Join(ctypes, ", ")),
33+
RequestPath: request.URL.Path,
34+
RequestMethod: request.Method,
35+
SpecPath: specPath,
3336
}
3437
}
3538

36-
func OperationNotFound(pathItem *v3.PathItem, request *http.Request, method string) *ValidationError {
39+
func OperationNotFound(pathItem *v3.PathItem, request *http.Request, method string, specPath string) *ValidationError {
3740
return &ValidationError{
3841
ValidationType: helpers.RequestValidation,
3942
ValidationSubType: helpers.RequestMissingOperation,
4043
Message: fmt.Sprintf("%s operation request content type '%s' does not exist",
4144
request.Method, method),
42-
Reason: fmt.Sprintf("The path was found, but there was no '%s' method found in the spec", request.Method),
43-
SpecLine: pathItem.GoLow().KeyNode.Line,
44-
SpecCol: pathItem.GoLow().KeyNode.Column,
45-
Context: pathItem,
46-
HowToFix: HowToFixPathMethod,
45+
Reason: fmt.Sprintf("The path was found, but there was no '%s' method found in the spec", request.Method),
46+
SpecLine: pathItem.GoLow().KeyNode.Line,
47+
SpecCol: pathItem.GoLow().KeyNode.Column,
48+
Context: pathItem,
49+
HowToFix: HowToFixPathMethod,
50+
RequestPath: request.URL.Path,
51+
RequestMethod: request.Method,
52+
SpecPath: specPath,
4753
}
4854
}

errors/validation_error.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ type ValidationError struct {
7575
// HowToFix is a human-readable message describing how to fix the error.
7676
HowToFix string `json:"howToFix" yaml:"howToFix"`
7777

78+
// RequestPath is the path of the request
79+
RequestPath string `json:"requestPath" yaml:"requestPath"`
80+
81+
// SpecPath is the path from the specification that corresponds to the request
82+
SpecPath string `json:"specPath" yaml:"specPath"`
83+
84+
// RequestMethod is the HTTP method of the request
85+
RequestMethod string `json:"requestMethod" yaml:"requestMethod"`
86+
7887
// SchemaValidationErrors is a slice of SchemaValidationFailure objects that describe the validation errors
7988
// This is only populated whe the validation type is against a schema.
8089
SchemaValidationErrors []*SchemaValidationFailure `json:"validationErrors,omitempty" yaml:"validationErrors,omitempty"`

parameters/cookie_parameters.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@ func (v *paramValidator) ValidateCookieParams(request *http.Request) (bool, []*e
1919

2020
// find path
2121
var pathItem *v3.PathItem
22+
var foundPath string
2223
var errs []*errors.ValidationError
24+
2325
if v.pathItem == nil {
24-
pathItem, errs, _ = paths.FindPath(request, v.document)
26+
pathItem, errs, foundPath = paths.FindPath(request, v.document)
2527
if pathItem == nil || errs != nil {
2628
v.errors = errs
2729
return false, errs
2830
}
2931
} else {
3032
pathItem = v.pathItem
33+
foundPath = v.pathValue
3134
}
3235

3336
// extract params for the operation
@@ -121,6 +124,9 @@ func (v *paramValidator) ValidateCookieParams(request *http.Request) (bool, []*e
121124
}
122125
}
123126
}
127+
128+
errors.PopulateValidationErrors(validationErrors, request, foundPath)
129+
124130
if len(validationErrors) > 0 {
125131
return false, validationErrors
126132
}

parameters/cookie_parameters_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ paths:
3535

3636
assert.False(t, valid)
3737
assert.Len(t, errors, 1)
38+
assert.Equal(t, request.Method, errors[0].RequestMethod)
39+
assert.Equal(t, request.URL.Path, errors[0].RequestPath)
40+
assert.Equal(t, "", errors[0].SpecPath)
3841
}
3942

4043
func TestNewValidator_CookieParamNumberValid(t *testing.T) {
@@ -202,6 +205,9 @@ paths:
202205
assert.Len(t, errors, 1)
203206
assert.Equal(t,
204207
"Instead of 'milk', use one of the allowed values: 'beef, chicken, pea protein'", errors[0].HowToFix)
208+
assert.Equal(t, request.Method, errors[0].RequestMethod)
209+
assert.Equal(t, request.URL.Path, errors[0].RequestPath)
210+
assert.Equal(t, "/burgers/beef", errors[0].SpecPath)
205211
}
206212

207213
func TestNewValidator_CookieParamBooleanInvalid(t *testing.T) {

parameters/header_parameters.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ import (
1919
func (v *paramValidator) ValidateHeaderParams(request *http.Request) (bool, []*errors.ValidationError) {
2020
// find path
2121
var pathItem *v3.PathItem
22+
var specPath string
2223
var errs []*errors.ValidationError
2324
if v.pathItem == nil {
24-
pathItem, errs, _ = paths.FindPath(request, v.document)
25+
pathItem, errs, specPath = paths.FindPath(request, v.document)
2526
if pathItem == nil || errs != nil {
2627
v.errors = errs
2728
return false, errs
2829
}
2930
} else {
3031
pathItem = v.pathItem
32+
specPath = v.pathValue
3133
}
3234

3335
// extract params for the operation
@@ -143,6 +145,8 @@ func (v *paramValidator) ValidateHeaderParams(request *http.Request) (bool, []*e
143145
}
144146
}
145147

148+
errors.PopulateValidationErrors(validationErrors, request, specPath)
149+
146150
if len(validationErrors) > 0 {
147151
return false, validationErrors
148152
}

parameters/header_parameters_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ paths:
3636
assert.False(t, valid)
3737
assert.Equal(t, 1, len(errors))
3838
assert.Equal(t, "Header parameter 'bash' is missing", errors[0].Message)
39+
assert.Equal(t, request.Method, errors[0].RequestMethod)
40+
assert.Equal(t, request.URL.Path, errors[0].RequestPath)
41+
assert.Equal(t, "/bish/bosh", errors[0].SpecPath)
3942
}
4043

4144
func TestNewValidator_HeaderPathMissing(t *testing.T) {
@@ -63,6 +66,9 @@ paths:
6366
assert.False(t, valid)
6467
assert.Equal(t, 1, len(errors))
6568
assert.Equal(t, "GET Path '/I/do/not/exist' not found", errors[0].Message)
69+
assert.Equal(t, request.Method, errors[0].RequestMethod)
70+
assert.Equal(t, request.URL.Path, errors[0].RequestPath)
71+
assert.Equal(t, "", errors[0].SpecPath)
6672
}
6773

6874
func TestNewValidator_HeaderParamDefaultEncoding_InvalidParamTypeNumber(t *testing.T) {

parameters/path_parameters.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ func (v *paramValidator) ValidatePathParams(request *http.Request) (bool, []*err
276276
}
277277
}
278278
}
279+
280+
errors.PopulateValidationErrors(validationErrors, request, foundPath)
281+
279282
if len(validationErrors) > 0 {
280283
return false, validationErrors
281284
}

parameters/path_parameters_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ paths:
6868
assert.False(t, valid)
6969
assert.Len(t, errors, 2)
7070
assert.Equal(t, "Path array parameter 'burgerIds' is not a valid number", errors[0].Message)
71+
assert.Equal(t, request.Method, errors[0].RequestMethod)
72+
assert.Equal(t, request.URL.Path, errors[0].RequestPath)
73+
assert.Equal(t, "/burgers/{burgerIds*}/locate", errors[0].SpecPath)
7174
}
7275

7376
func TestNewValidator_SimpleArrayEncodedPath_InvalidBool(t *testing.T) {

parameters/query_parameters.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@ import (
2222
func (v *paramValidator) ValidateQueryParams(request *http.Request) (bool, []*errors.ValidationError) {
2323
// find path
2424
var pathItem *v3.PathItem
25+
var foundPath string
2526
var errs []*errors.ValidationError
2627
if v.pathItem == nil {
27-
pathItem, errs, _ = paths.FindPath(request, v.document)
28+
pathItem, errs, foundPath = paths.FindPath(request, v.document)
2829
if pathItem == nil || errs != nil {
2930
v.errors = errs
3031
return false, errs
3132
}
3233
} else {
3334
pathItem = v.pathItem
35+
foundPath = v.pathValue
3436
}
3537

3638
// extract params for the operation
@@ -211,6 +213,8 @@ doneLooking:
211213
}
212214
}
213215

216+
errors.PopulateValidationErrors(validationErrors, request, foundPath)
217+
214218
v.errors = validationErrors
215219
if len(validationErrors) > 0 {
216220
return false, validationErrors

0 commit comments

Comments
 (0)