Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
752 changes: 376 additions & 376 deletions errors/parameter_errors.go

Large diffs are not rendered by default.

44 changes: 22 additions & 22 deletions errors/request_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,29 @@
package errors

import (
"fmt"
"github.com/pb33f/libopenapi-validator/helpers"
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
"net/http"
"strings"
"fmt"
"github.com/pb33f/libopenapi-validator/helpers"
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
"net/http"
"strings"
)

func RequestContentTypeNotFound(op *v3.Operation, request *http.Request) *ValidationError {
ct := request.Header.Get(helpers.ContentTypeHeader)
var ctypes []string
for k := range op.RequestBody.Content {
ctypes = append(ctypes, k)
}
return &ValidationError{
ValidationType: helpers.RequestBodyValidation,
ValidationSubType: helpers.RequestBodyContentType,
Message: fmt.Sprintf("%s operation request content type '%s' does not exist",
request.Method, ct),
Reason: fmt.Sprintf("The content type '%s' of the %s request submitted has not "+
"been defined, it's an unknown type", ct, request.Method),
SpecLine: op.RequestBody.GoLow().Content.KeyNode.Line,
SpecCol: op.RequestBody.GoLow().Content.KeyNode.Column,
Context: op,
HowToFix: fmt.Sprintf(HowToFixInvalidContentType, len(op.RequestBody.Content), strings.Join(ctypes, ", ")),
}
ct := request.Header.Get(helpers.ContentTypeHeader)
var ctypes []string
for k := range op.RequestBody.Content {
ctypes = append(ctypes, k)
}
return &ValidationError{
ValidationType: helpers.RequestBodyValidation,
ValidationSubType: helpers.RequestBodyContentType,
Message: fmt.Sprintf("%s operation request content type '%s' does not exist",
request.Method, ct),
Reason: fmt.Sprintf("The content type '%s' of the %s request submitted has not "+
"been defined, it's an unknown type", ct, request.Method),
SpecLine: op.RequestBody.GoLow().Content.KeyNode.Line,
SpecCol: op.RequestBody.GoLow().Content.KeyNode.Column,
Context: op,
HowToFix: fmt.Sprintf(HowToFixInvalidContentType, len(op.RequestBody.Content), strings.Join(ctypes, ", ")),
}
}
116 changes: 63 additions & 53 deletions errors/validation_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,95 +4,105 @@
package errors

import (
"fmt"
"github.com/santhosh-tekuri/jsonschema/v5"
"fmt"
"github.com/santhosh-tekuri/jsonschema/v5"
)

// SchemaValidationFailure is a wrapper around the jsonschema.ValidationError object, to provide a more
// user-friendly way to break down what went wrong.
type SchemaValidationFailure struct {
// Reason is a human-readable message describing the reason for the error.
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
// Reason is a human-readable message describing the reason for the error.
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`

// Location is the XPath-like location of the validation failure
Location string `json:"location,omitempty" yaml:"location,omitempty"`
// Location is the XPath-like location of the validation failure
Location string `json:"location,omitempty" yaml:"location,omitempty"`

// DeepLocation is the path to the validation failure as exposed by the jsonschema library.
DeepLocation string `json:"deepLocation,omitempty" yaml:"deepLocation,omitempty"`
// DeepLocation is the path to the validation failure as exposed by the jsonschema library.
DeepLocation string `json:"deepLocation,omitempty" yaml:"deepLocation,omitempty"`

// AbsoluteLocation is the absolute path to the validation failure as exposed by the jsonschema library.
AbsoluteLocation string `json:"absoluteLocation,omitempty" yaml:"absoluteLocation,omitempty"`
// AbsoluteLocation is the absolute path to the validation failure as exposed by the jsonschema library.
AbsoluteLocation string `json:"absoluteLocation,omitempty" yaml:"absoluteLocation,omitempty"`

// Line is the line number where the violation occurred. This may a local line number
// if the validation is a schema (only schemas are validated locally, so the line number will be relative to
// the Context object held by the ValidationError object).
Line int `json:"line,omitempty" yaml:"line,omitempty"`
// Line is the line number where the violation occurred. This may a local line number
// if the validation is a schema (only schemas are validated locally, so the line number will be relative to
// the Context object held by the ValidationError object).
Line int `json:"line,omitempty" yaml:"line,omitempty"`

// Column is the column number where the violation occurred. This may a local column number
// if the validation is a schema (only schemas are validated locally, so the column number will be relative to
// the Context object held by the ValidationError object).
Column int `json:"column,omitempty" yaml:"column,omitempty"`
// Column is the column number where the violation occurred. This may a local column number
// if the validation is a schema (only schemas are validated locally, so the column number will be relative to
// the Context object held by the ValidationError object).
Column int `json:"column,omitempty" yaml:"column,omitempty"`

// ReferenceSchema is the schema that was referenced in the validation failure.
ReferenceSchema string `json:"referenceSchema,omitempty" yaml:"referenceSchema,omitempty"`
// ReferenceSchema is the schema that was referenced in the validation failure.
ReferenceSchema string `json:"referenceSchema,omitempty" yaml:"referenceSchema,omitempty"`

// ReferenceObject is the object that was referenced in the validation failure.
ReferenceObject string `json:"referenceObject,omitempty" yaml:"referenceObject,omitempty"`
// ReferenceObject is the object that was referenced in the validation failure.
ReferenceObject string `json:"referenceObject,omitempty" yaml:"referenceObject,omitempty"`

// The original error object, which is a jsonschema.ValidationError object.
OriginalError *jsonschema.ValidationError `json:"-" yaml:"-"`
// The original error object, which is a jsonschema.ValidationError object.
OriginalError *jsonschema.ValidationError `json:"-" yaml:"-"`
}

// Error returns a string representation of the error
func (s *SchemaValidationFailure) Error() string {
return fmt.Sprintf("Reason: %s, Location: %s", s.Reason, s.Location)
return fmt.Sprintf("Reason: %s, Location: %s", s.Reason, s.Location)
}

// ValidationError is a struct that contains all the information about a validation error.
type ValidationError struct {

// Message is a human-readable message describing the error.
Message string `json:"message" yaml:"message"`
// Message is a human-readable message describing the error.
Message string `json:"message" yaml:"message"`

// Reason is a human-readable message describing the reason for the error.
Reason string `json:"reason" yaml:"reason"`
// Reason is a human-readable message describing the reason for the error.
Reason string `json:"reason" yaml:"reason"`

// ValidationType is a string that describes the type of validation that failed.
ValidationType string `json:"validationType" yaml:"validationType"`
// ValidationType is a string that describes the type of validation that failed.
ValidationType string `json:"validationType" yaml:"validationType"`

// ValidationSubType is a string that describes the subtype of validation that failed.
ValidationSubType string `json:"validationSubType" yaml:"validationSubType"`
// ValidationSubType is a string that describes the subtype of validation that failed.
ValidationSubType string `json:"validationSubType" yaml:"validationSubType"`

// SpecLine is the line number in the spec where the error occurred.
SpecLine int `json:"specLine" yaml:"specLine"`
// SpecLine is the line number in the spec where the error occurred.
SpecLine int `json:"specLine" yaml:"specLine"`

// SpecCol is the column number in the spec where the error occurred.
SpecCol int `json:"specColumn" yaml:"specColumn"`
// SpecCol is the column number in the spec where the error occurred.
SpecCol int `json:"specColumn" yaml:"specColumn"`

// HowToFix is a human-readable message describing how to fix the error.
HowToFix string `json:"howToFix" yaml:"howToFix"`
// HowToFix is a human-readable message describing how to fix the error.
HowToFix string `json:"howToFix" yaml:"howToFix"`

// SchemaValidationErrors is a slice of SchemaValidationFailure objects that describe the validation errors
// This is only populated whe the validation type is against a schema.
SchemaValidationErrors []*SchemaValidationFailure `json:"validationErrors,omitempty" yaml:"validationErrors,omitempty"`
// SchemaValidationErrors is a slice of SchemaValidationFailure objects that describe the validation errors
// This is only populated whe the validation type is against a schema.
SchemaValidationErrors []*SchemaValidationFailure `json:"validationErrors,omitempty" yaml:"validationErrors,omitempty"`

// Context is the object that the validation error occurred on. This is usually a pointer to a schema
// or a parameter object.
Context interface{} `json:"-" yaml:"-"`
// Context is the object that the validation error occurred on. This is usually a pointer to a schema
// or a parameter object.
Context interface{} `json:"-" yaml:"-"`
}

// Error returns a string representation of the error
func (v *ValidationError) Error() string {
if v.SchemaValidationErrors != nil {
return fmt.Sprintf("Error: %s, Reason: %s, Validation Errors: %s, Line: %d, Column: %d",
v.Message, v.Reason, v.SchemaValidationErrors, v.SpecLine, v.SpecCol)
} else {
return fmt.Sprintf("Error: %s, Reason: %s, Line: %d, Column: %d",
v.Message, v.Reason, v.SpecLine, v.SpecCol)
}
if v.SchemaValidationErrors != nil {
if v.SpecLine > 0 && v.SpecCol > 0 {
return fmt.Sprintf("Error: %s, Reason: %s, Validation Errors: %s, Line: %d, Column: %d",
v.Message, v.Reason, v.SchemaValidationErrors, v.SpecLine, v.SpecCol)
} else {
return fmt.Sprintf("Error: %s, Reason: %s, Validation Errors: %s",
v.Message, v.Reason, v.SchemaValidationErrors)
}
} else {
if v.SpecLine > 0 && v.SpecCol > 0 {
return fmt.Sprintf("Error: %s, Reason: %s, Line: %d, Column: %d",
v.Message, v.Reason, v.SpecLine, v.SpecCol)
} else {
return fmt.Sprintf("Error: %s, Reason: %s",
v.Message, v.Reason)
}
}
}

// IsPathMissingError returns true if the error has a ValidationType of "path" and a ValidationSubType of "missing"
func (v *ValidationError) IsPathMissingError() bool {
return v.ValidationType == "path" && v.ValidationSubType == "missing"
return v.ValidationType == "path" && v.ValidationSubType == "missing"
}
83 changes: 42 additions & 41 deletions helpers/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,46 @@
package helpers

const (
ParameterValidation = "parameter"
ParameterValidationPath = "path"
ParameterValidationQuery = "query"
ParameterValidationHeader = "header"
ParameterValidationCookie = "cookie"
RequestBodyValidation = "requestBody"
Schema = "schema"
ResponseBodyValidation = "response"
RequestBodyContentType = "contentType"
ResponseBodyResponseCode = "statusCode"
SpaceDelimited = "spaceDelimited"
PipeDelimited = "pipeDelimited"
DefaultDelimited = "default"
MatrixStyle = "matrix"
LabelStyle = "label"
Pipe = "|"
Comma = ","
Space = " "
SemiColon = ";"
Asterisk = "*"
Period = "."
Equals = "="
Integer = "integer"
Number = "number"
Slash = "/"
Object = "object"
String = "string"
Array = "array"
Boolean = "boolean"
DeepObject = "deepObject"
Header = "header"
Cookie = "cookie"
Path = "path"
Form = "form"
Query = "query"
JSONContentType = "application/json"
JSONType = "json"
ContentTypeHeader = "Content-Type"
Charset = "charset"
Boundary = "boundary"
FailSegment = "**&&FAIL&&**"
ParameterValidation = "parameter"
ParameterValidationPath = "path"
ParameterValidationQuery = "query"
ParameterValidationHeader = "header"
ParameterValidationCookie = "cookie"
RequestBodyValidation = "requestBody"
Schema = "schema"
ResponseBodyValidation = "response"
RequestBodyContentType = "contentType"
ResponseBodyResponseCode = "statusCode"
SpaceDelimited = "spaceDelimited"
PipeDelimited = "pipeDelimited"
DefaultDelimited = "default"
MatrixStyle = "matrix"
LabelStyle = "label"
Pipe = "|"
Comma = ","
Space = " "
SemiColon = ";"
Asterisk = "*"
Period = "."
Equals = "="
Integer = "integer"
Number = "number"
Slash = "/"
Object = "object"
String = "string"
Array = "array"
Boolean = "boolean"
DeepObject = "deepObject"
Header = "header"
Cookie = "cookie"
Path = "path"
Form = "form"
Query = "query"
JSONContentType = "application/json"
JSONType = "json"
ContentTypeHeader = "Content-Type"
Charset = "charset"
Boundary = "boundary"
Preferred = "preferred"
FailSegment = "**&&FAIL&&**"
)
82 changes: 41 additions & 41 deletions helpers/operation_utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,56 @@
package helpers

import (
"github.com/pb33f/libopenapi/datamodel/high/v3"
"net/http"
"strings"
"github.com/pb33f/libopenapi/datamodel/high/v3"
"net/http"
"strings"
)

// ExtractOperation extracts the operation from the path item based on the request method. If there is no
// matching operation found, then nil is returned.
func ExtractOperation(request *http.Request, item *v3.PathItem) *v3.Operation {
switch request.Method {
case http.MethodGet:
return item.Get
case http.MethodPost:
return item.Post
case http.MethodPut:
return item.Put
case http.MethodDelete:
return item.Delete
case http.MethodOptions:
return item.Options
case http.MethodHead:
return item.Head
case http.MethodPatch:
return item.Patch
case http.MethodTrace:
return item.Trace
}
return nil
switch request.Method {
case http.MethodGet:
return item.Get
case http.MethodPost:
return item.Post
case http.MethodPut:
return item.Put
case http.MethodDelete:
return item.Delete
case http.MethodOptions:
return item.Options
case http.MethodHead:
return item.Head
case http.MethodPatch:
return item.Patch
case http.MethodTrace:
return item.Trace
}
return nil
}

// ExtractContentType extracts the content type from the request header. First return argument is the content type
// of the request.The second (optional) argument is the charset of the request. The third (optional)
// argument is the boundary of the type (only used with forms really).
func ExtractContentType(contentType string) (string, string, string) {
var charset, boundary string
if strings.ContainsRune(contentType, ';') {
segs := strings.Split(contentType, SemiColon)
contentType = strings.TrimSpace(segs[0])
for _, v := range segs[1:] {
kv := strings.Split(v, Equals)
if len(kv) == 2 {
if strings.TrimSpace(strings.ToLower(kv[0])) == Charset {
charset = strings.TrimSpace(kv[1])
}
if strings.TrimSpace(strings.ToLower(kv[0])) == Boundary {
boundary = strings.TrimSpace(kv[1])
}
}
}
} else {
contentType = strings.TrimSpace(contentType)
}
return contentType, charset, boundary
var charset, boundary string
if strings.ContainsRune(contentType, ';') {
segs := strings.Split(contentType, SemiColon)
contentType = strings.TrimSpace(segs[0])
for _, v := range segs[1:] {
kv := strings.Split(v, Equals)
if len(kv) == 2 {
if strings.TrimSpace(strings.ToLower(kv[0])) == Charset {
charset = strings.TrimSpace(kv[1])
}
if strings.TrimSpace(strings.ToLower(kv[0])) == Boundary {
boundary = strings.TrimSpace(kv[1])
}
}
}
} else {
contentType = strings.TrimSpace(contentType)
}
return contentType, charset, boundary
}
Loading