Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
37 changes: 19 additions & 18 deletions errors/validation_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,29 @@ import (
"github.com/santhosh-tekuri/jsonschema/v6"
)

// SchemaValidationFailure is a wrapper around the jsonschema.ValidationError object, to provide a more
// user-friendly way to break down what went wrong.
// SchemaValidationFailure describes any failure that occurs when validating data
// against either an OpenAPI or JSON Schema. It aims to be a more user-friendly
// representation of the error than what is provided by the jsonschema library.
type SchemaValidationFailure struct {
// 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"`
// InstancePath is the raw path segments from the root to the failing field
InstancePath []string `json:"instancePath,omitempty" yaml:"instancePath,omitempty"`

// FieldName is the name of the specific field that failed validation (last segment of the path)
FieldName string `json:"fieldName,omitempty" yaml:"fieldName,omitempty"`

// FieldPath is the JSONPath representation of the field location (e.g., "$.user.email")
// FieldPath is the JSONPath representation of the field location that failed validation (e.g., "$.user.email")
FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"`

// InstancePath is the raw path segments from the root to the failing field
InstancePath []string `json:"instancePath,omitempty" yaml:"instancePath,omitempty"`

// DeepLocation is the path to the validation failure as exposed by the jsonschema library.
DeepLocation string `json:"deepLocation,omitempty" yaml:"deepLocation,omitempty"`
// KeywordLocation is the relative path to the JsonSchema keyword that failed validation
// This will be empty if the validation failure did not originate from JSON Schema validation
KeywordLocation string `json:"keywordLocation,omitempty" yaml:"keywordLocation,omitempty"`

// AbsoluteLocation is the absolute path to the validation failure as exposed by the jsonschema library.
AbsoluteLocation string `json:"absoluteLocation,omitempty" yaml:"absoluteLocation,omitempty"`
// AbsoluteKeywordLocation is the absolute path to the validation failure as exposed by the jsonschema library.
// This will be empty if the validation failure did not originate from JSON Schema validation
AbsoluteKeywordLocation string `json:"absoluteKeywordLocation,omitempty" yaml:"absoluteKeywordLocation,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
Expand All @@ -46,14 +46,15 @@ type SchemaValidationFailure struct {
// 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 is the object that failed schema validation
ReferenceObject string `json:"referenceObject,omitempty" yaml:"referenceObject,omitempty"`

// ReferenceExample is an example object generated from the schema that was referenced in the validation failure.
ReferenceExample string `json:"referenceExample,omitempty" yaml:"referenceExample,omitempty"`
// The original jsonschema.ValidationError object, if the schema failure originated from the jsonschema library.
OriginalJsonSchemaError *jsonschema.ValidationError `json:"-" yaml:"-"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the ReferenceExample?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change the name of the OriginalError to OriginalJsonSchemaError? it's the same type, I think just updating the docs is sufficient here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! Sure, let me explain.

The overall goal here is to clarify the responsibility of the SchemaValidationFailure type as much as possible. In this case, I believe that its responsibility will be broadened a bit.

  • Before: A thin wrapper around jsonschema.ValidationError, focused only on jsonschema issues.
  • After: A more comprehensive object that can report on any validation failure, whether it's a JSON Schema constraint or a different, OpenAPI-specific constraint.
  1. On renaming OriginalError to OriginalJsonSchemaError: I made this change to make the field's purpose more explicit. It clarifies that this field will only be populated when the failure is due to a JSON Schema rule. For other OpenAPI-specific validation failures, this field will be nil.

  2. On removing ReferenceExample: I didn't see it referenced anywhere else in the code so I thought it should be removed for clarity -- It was introduced here but it wasn't clear to me if the intended use was ever carried out. Would you mind explaining its intended use by library users? Happy to add it back if that makes sense

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. OK. I can buy that.

  2. I need it.

https://github.com/pb33f/wiretap/blob/main/ui/src/components/violation/violation-details.ts#L165

I have not yet built what I need, but I need that property when I do.


// The original error object, which is a jsonschema.ValidationError object.
OriginalError *jsonschema.ValidationError `json:"-" yaml:"-"`
// DEPRECATED in favor of explicit use of FieldPath & InstancePath
// Location is the XPath-like location of the validation failure
Location string `json:"location,omitempty" yaml:"location,omitempty"`
}

// Error returns a string representation of the error
Expand Down Expand Up @@ -97,7 +98,7 @@ type ValidationError struct {
ParameterName string `json:"parameterName,omitempty" yaml:"parameterName,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.
// This is only populated when 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
Expand Down
12 changes: 6 additions & 6 deletions parameters/validate_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,12 @@ func formatJsonSchemaValidationError(schema *base.Schema, scErrs *jsonschema.Val
}

fail := &errors.SchemaValidationFailure{
Reason: errMsg,
Location: er.KeywordLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
OriginalError: scErrs,
Reason: errMsg,
Location: er.KeywordLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
OriginalJsonSchemaError: scErrs,
}
if schema != nil {
rendered, err := schema.RenderInline()
Expand Down
16 changes: 8 additions & 8 deletions requests/validate_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,14 @@ func ValidateRequestSchema(
errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{}))

violation := &errors.SchemaValidationFailure{
Reason: errMsg,
Location: er.KeywordLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
ReferenceSchema: string(renderedSchema),
ReferenceObject: referenceObject,
OriginalError: jk,
Reason: errMsg,
Location: er.KeywordLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
ReferenceSchema: string(renderedSchema),
ReferenceObject: referenceObject,
OriginalJsonSchemaError: jk,
}
// if we have a location within the schema, add it to the error
if located != nil {
Expand Down
16 changes: 8 additions & 8 deletions responses/validate_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,14 @@ func ValidateResponseSchema(
}

violation := &errors.SchemaValidationFailure{
Reason: errMsg,
Location: er.KeywordLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
ReferenceSchema: string(renderedSchema),
ReferenceObject: referenceObject,
OriginalError: jk,
Reason: errMsg,
Location: er.KeywordLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
ReferenceSchema: string(renderedSchema),
ReferenceObject: referenceObject,
OriginalJsonSchemaError: jk,
}
// if we have a location within the schema, add it to the error
if located != nil {
Expand Down
16 changes: 8 additions & 8 deletions schema_validation/validate_document.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ func ValidateOpenAPIDocument(doc libopenapi.Document, opts ...config.Option) (bo
// locate the violated property in the schema
located := LocateSchemaPropertyNodeByJSONPath(info.RootNode.Content[0], er.InstanceLocation)
violation := &liberrors.SchemaValidationFailure{
Reason: errMsg,
Location: er.InstanceLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
DeepLocation: er.KeywordLocation,
AbsoluteLocation: er.AbsoluteKeywordLocation,
OriginalError: jk,
Reason: errMsg,
Location: er.InstanceLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
KeywordLocation: er.KeywordLocation,
AbsoluteKeywordLocation: er.AbsoluteKeywordLocation,
OriginalJsonSchemaError: jk,
}

// if we have a location within the schema, add it to the error
Expand Down
20 changes: 10 additions & 10 deletions schema_validation/validate_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,16 +300,16 @@ func extractBasicErrors(schFlatErrs []jsonschema.OutputUnit,
}

violation := &liberrors.SchemaValidationFailure{
Reason: errMsg,
Location: er.InstanceLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
DeepLocation: er.KeywordLocation,
AbsoluteLocation: er.AbsoluteKeywordLocation,
ReferenceSchema: string(renderedSchema),
ReferenceObject: referenceObject,
OriginalError: jk,
Reason: errMsg,
Location: er.InstanceLocation,
FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation),
FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation),
InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation),
KeywordLocation: er.KeywordLocation,
AbsoluteKeywordLocation: er.AbsoluteKeywordLocation,
ReferenceSchema: string(renderedSchema),
ReferenceObject: referenceObject,
OriginalJsonSchemaError: jk,
}
// if we have a location within the schema, add it to the error
if located != nil {
Expand Down