Skip to content

Commit 38c5e03

Browse files
SebTardifdaveshanley
authored andcommitted
Replace bare type assertions with errors.As in request/response validation
1 parent 1e05aa6 commit 38c5e03

2 files changed

Lines changed: 163 additions & 158 deletions

File tree

requests/validate_request.go

Lines changed: 83 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package requests
66
import (
77
"bytes"
88
"encoding/json"
9+
"errors"
910
"fmt"
1011
"io"
1112
"net/http"
@@ -22,7 +23,7 @@ import (
2223

2324
"github.com/pb33f/libopenapi-validator/cache"
2425
"github.com/pb33f/libopenapi-validator/config"
25-
"github.com/pb33f/libopenapi-validator/errors"
26+
liberrors "github.com/pb33f/libopenapi-validator/errors"
2627
"github.com/pb33f/libopenapi-validator/helpers"
2728
"github.com/pb33f/libopenapi-validator/schema_validation"
2829
"github.com/pb33f/libopenapi-validator/strict"
@@ -133,23 +134,23 @@ func readAndResetRequestBody(request *http.Request) []byte {
133134
// ValidateRequestSchema will validate a http.Request pointer against a schema.
134135
// If validation fails, it will return a list of validation errors as the second return value.
135136
// The schema will be stored and reused from cache if available, otherwise it will be compiled on each call.
136-
func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.ValidationError) {
137+
func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*liberrors.ValidationError) {
137138
validationOptions := config.NewValidationOptions(input.Options...)
138-
var validationErrors []*errors.ValidationError
139+
var validationErrors []*liberrors.ValidationError
139140
var renderedSchema, jsonSchema []byte
140141
var referenceSchema string
141142
var compiledSchema *jsonschema.Schema
142143
var cachedNode *yaml.Node
143144

144145
if input.Schema == nil {
145-
return false, []*errors.ValidationError{{
146+
return false, []*liberrors.ValidationError{{
146147
ValidationType: helpers.RequestBodyValidation,
147148
ValidationSubType: helpers.Schema,
148149
Message: "schema is nil",
149150
Reason: "The schema to validate against is nil",
150151
}}
151152
} else if input.Schema.GoLow() == nil {
152-
return false, []*errors.ValidationError{{
153+
return false, []*liberrors.ValidationError{{
153154
ValidationType: helpers.RequestBodyValidation,
154155
ValidationSubType: helpers.Schema,
155156
Message: "schema cannot be rendered",
@@ -177,11 +178,11 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
177178

178179
// If rendering failed (e.g., circular reference), return the render error
179180
if renderErr != nil {
180-
violation := &errors.SchemaValidationFailure{
181+
violation := &liberrors.SchemaValidationFailure{
181182
Reason: renderErr.Error(),
182183
ReferenceSchema: referenceSchema,
183184
}
184-
validationErrors = append(validationErrors, &errors.ValidationError{
185+
validationErrors = append(validationErrors, &liberrors.ValidationError{
185186
ValidationType: helpers.RequestBodyValidation,
186187
ValidationSubType: helpers.Schema,
187188
Message: fmt.Sprintf("%s request body for '%s' failed schema rendering",
@@ -190,8 +191,8 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
190191
renderErr.Error()),
191192
SpecLine: 1,
192193
SpecCol: 0,
193-
SchemaValidationErrors: []*errors.SchemaValidationFailure{violation},
194-
HowToFix: errors.HowToFixInvalidRenderedSchema,
194+
SchemaValidationErrors: []*liberrors.SchemaValidationFailure{violation},
195+
HowToFix: liberrors.HowToFixInvalidRenderedSchema,
195196
Context: referenceSchema,
196197
})
197198
return false, validationErrors
@@ -208,7 +209,7 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
208209
input.Version,
209210
)
210211
if err != nil {
211-
validationErrors = append(validationErrors, &errors.ValidationError{
212+
validationErrors = append(validationErrors, &liberrors.ValidationError{
212213
ValidationType: helpers.RequestBodyValidation,
213214
ValidationSubType: helpers.Schema,
214215
Message: fmt.Sprintf("%s request body for '%s' failed schema compilation",
@@ -245,15 +246,15 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
245246
err := json.Unmarshal(requestBody, &decodedObj)
246247
if err != nil {
247248
// cannot decode the request body, so it's not valid
248-
validationErrors = append(validationErrors, &errors.ValidationError{
249+
validationErrors = append(validationErrors, &liberrors.ValidationError{
249250
ValidationType: helpers.RequestBodyValidation,
250251
ValidationSubType: helpers.Schema,
251252
Message: fmt.Sprintf("%s request body for '%s' failed to validate schema",
252253
request.Method, request.URL.Path),
253254
Reason: fmt.Sprintf("The request body cannot be decoded: %s", err.Error()),
254255
SpecLine: 1,
255256
SpecCol: 0,
256-
HowToFix: errors.HowToFixInvalidSchema,
257+
HowToFix: liberrors.HowToFixInvalidSchema,
257258
Context: schema,
258259
})
259260
return false, validationErrors
@@ -281,15 +282,15 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
281282
}
282283
}
283284

284-
validationErrors = append(validationErrors, &errors.ValidationError{
285+
validationErrors = append(validationErrors, &liberrors.ValidationError{
285286
ValidationType: helpers.RequestBodyValidation,
286287
ValidationSubType: helpers.Schema,
287288
Message: fmt.Sprintf("%s request body is empty for '%s'",
288289
request.Method, request.URL.Path),
289290
Reason: "The request body is empty but there is a schema defined",
290291
SpecLine: line,
291292
SpecCol: col,
292-
HowToFix: errors.HowToFixInvalidSchema,
293+
HowToFix: liberrors.HowToFixInvalidSchema,
293294
Context: schema,
294295
})
295296
return false, validationErrors
@@ -298,80 +299,81 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
298299
// validate the object against the schema
299300
scErrs := compiledSchema.Validate(decodedObj)
300301
if scErrs != nil {
301-
302-
jk := scErrs.(*jsonschema.ValidationError)
303-
304-
// flatten the validationErrors
305-
schFlatErrs := jk.BasicOutput().Errors
306-
var schemaValidationErrors []*errors.SchemaValidationFailure
307-
308-
// Use cached node if available, otherwise parse
309-
renderedNode := cachedNode
310-
if renderedNode == nil {
311-
renderedNode = new(yaml.Node)
312-
_ = yaml.Unmarshal(renderedSchema, renderedNode)
313-
}
314-
for q := range schFlatErrs {
315-
er := schFlatErrs[q]
316-
317-
errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{}))
318-
319-
if er.KeywordLocation == "" || helpers.IgnoreRegex.MatchString(errMsg) {
320-
continue // ignore this error, it's useless tbh, utter noise.
302+
var jk *jsonschema.ValidationError
303+
var schemaValidationErrors []*liberrors.SchemaValidationFailure
304+
305+
if errors.As(scErrs, &jk) {
306+
// flatten the validationErrors
307+
schFlatErrs := jk.BasicOutput().Errors
308+
309+
// Use cached node if available, otherwise parse
310+
renderedNode := cachedNode
311+
if renderedNode == nil {
312+
renderedNode = new(yaml.Node)
313+
_ = yaml.Unmarshal(renderedSchema, renderedNode)
321314
}
322-
if er.Error != nil {
315+
for q := range schFlatErrs {
316+
er := schFlatErrs[q]
323317

324-
// locate the violated property in the schema
325-
var located *yaml.Node
326-
if len(renderedNode.Content) > 0 {
327-
located = schema_validation.LocateSchemaPropertyNodeByJSONPath(renderedNode.Content[0], er.KeywordLocation)
328-
}
318+
errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{}))
329319

330-
// extract the element specified by the instance
331-
val := instanceLocationRegex.FindStringSubmatch(er.InstanceLocation)
332-
var referenceObject string
320+
if er.KeywordLocation == "" || helpers.IgnoreRegex.MatchString(errMsg) {
321+
continue // ignore this error, it's useless tbh, utter noise.
322+
}
323+
if er.Error != nil {
333324

334-
if len(val) > 0 {
335-
referenceIndex, _ := strconv.Atoi(val[1])
336-
if reflect.ValueOf(decodedObj).Type().Kind() == reflect.Slice {
337-
found := decodedObj.([]any)[referenceIndex]
338-
recoded, _ := json.MarshalIndent(found, "", " ")
339-
referenceObject = string(recoded)
325+
// locate the violated property in the schema
326+
var located *yaml.Node
327+
if len(renderedNode.Content) > 0 {
328+
located = schema_validation.LocateSchemaPropertyNodeByJSONPath(renderedNode.Content[0], er.KeywordLocation)
340329
}
341-
}
342-
if referenceObject == "" {
343-
referenceObject = string(requestBody)
344-
}
345330

346-
errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{}))
331+
// extract the element specified by the instance
332+
val := instanceLocationRegex.FindStringSubmatch(er.InstanceLocation)
333+
var referenceObject string
347334

348-
violation := &errors.SchemaValidationFailure{
349-
Reason: errMsg,
350-
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
351-
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
352-
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
353-
KeywordLocation: er.KeywordLocation,
354-
ReferenceSchema: referenceSchema,
355-
ReferenceObject: referenceObject,
356-
OriginalJsonSchemaError: jk,
357-
}
358-
// if we have a location within the schema, add it to the error
359-
if located != nil {
360-
361-
line := located.Line
362-
// if the located node is a map or an array, then the actual human interpretable
363-
// line on which the violation occurred is the line of the key, not the value.
364-
if located.Kind == yaml.MappingNode || located.Kind == yaml.SequenceNode {
365-
if line > 0 {
366-
line--
335+
if len(val) > 0 {
336+
referenceIndex, _ := strconv.Atoi(val[1])
337+
if reflect.ValueOf(decodedObj).Type().Kind() == reflect.Slice {
338+
found := decodedObj.([]any)[referenceIndex]
339+
recoded, _ := json.MarshalIndent(found, "", " ")
340+
referenceObject = string(recoded)
367341
}
368342
}
343+
if referenceObject == "" {
344+
referenceObject = string(requestBody)
345+
}
369346

370-
// location of the violation within the rendered schema.
371-
violation.Line = line
372-
violation.Column = located.Column
347+
errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{}))
348+
349+
violation := &liberrors.SchemaValidationFailure{
350+
Reason: errMsg,
351+
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
352+
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
353+
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
354+
KeywordLocation: er.KeywordLocation,
355+
ReferenceSchema: referenceSchema,
356+
ReferenceObject: referenceObject,
357+
OriginalJsonSchemaError: jk,
358+
}
359+
// if we have a location within the schema, add it to the error
360+
if located != nil {
361+
362+
line := located.Line
363+
// if the located node is a map or an array, then the actual human interpretable
364+
// line on which the violation occurred is the line of the key, not the value.
365+
if located.Kind == yaml.MappingNode || located.Kind == yaml.SequenceNode {
366+
if line > 0 {
367+
line--
368+
}
369+
}
370+
371+
// location of the violation within the rendered schema.
372+
violation.Line = line
373+
violation.Column = located.Column
374+
}
375+
schemaValidationErrors = append(schemaValidationErrors, violation)
373376
}
374-
schemaValidationErrors = append(schemaValidationErrors, violation)
375377
}
376378
}
377379

@@ -383,7 +385,7 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
383385
}
384386

385387
// add the error to the list
386-
validationErrors = append(validationErrors, &errors.ValidationError{
388+
validationErrors = append(validationErrors, &liberrors.ValidationError{
387389
ValidationType: helpers.RequestBodyValidation,
388390
ValidationSubType: helpers.Schema,
389391
Message: fmt.Sprintf("%s request body for '%s' failed to validate schema",
@@ -393,7 +395,7 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
393395
SpecLine: line,
394396
SpecCol: col,
395397
SchemaValidationErrors: schemaValidationErrors,
396-
HowToFix: errors.HowToFixInvalidSchema,
398+
HowToFix: liberrors.HowToFixInvalidSchema,
397399
Context: schema,
398400
})
399401
}
@@ -418,14 +420,14 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
418420
switch undeclared.Type {
419421
case strict.TypeReadOnlyProperty:
420422
validationErrors = append(validationErrors,
421-
errors.ReadOnlyPropertyError(
423+
liberrors.ReadOnlyPropertyError(
422424
undeclared.Path, undeclared.Name, undeclared.Value,
423425
request.URL.Path, request.Method,
424426
undeclared.SpecLine, undeclared.SpecCol,
425427
))
426428
default:
427429
validationErrors = append(validationErrors,
428-
errors.UndeclaredPropertyError(
430+
liberrors.UndeclaredPropertyError(
429431
undeclared.Path,
430432
undeclared.Name,
431433
undeclared.Value,

0 commit comments

Comments
 (0)