Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
23 changes: 21 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
//
// Generally fluent With... style functions are used to establish the desired behavior.
type ValidationOptions struct {
RegexEngine jsonschema.RegexpEngine
RegexEngine jsonschema.RegexpEngine
FormatAssertions bool
ContentAssertions bool
}

// Option Enables an 'Options pattern' approach
Expand All @@ -15,7 +17,10 @@
// NewValidationOptions creates a new ValidationOptions instance with default values.
func NewValidationOptions(opts ...Option) *ValidationOptions {
// Create the set of default values
o := &ValidationOptions{}
o := &ValidationOptions{
FormatAssertions: false,
ContentAssertions: false,
}

// Apply any supplied overrides
for _, opt := range opts {
Expand All @@ -32,3 +37,17 @@
o.RegexEngine = engine
}
}

// WithFormatAssertions enables checks for 'format' assertions (such as date, date-time, uuid, etc)
func WithFormatAssertions() Option {
return func(o *ValidationOptions) {
o.FormatAssertions = true
}
}

// WithContentAssertions enables checks for contentType, contentEncoding, etc
func WithContentAssertions() Option {
return func(o *ValidationOptions) {
o.ContentAssertions = true
}

Check warning on line 52 in config/config.go

View check run for this annotation

Codecov / codecov/patch

config/config.go#L49-L52

Added lines #L49 - L52 were not covered by tests
}
55 changes: 55 additions & 0 deletions helpers/schema_compiler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package helpers

import (
"bytes"
"fmt"

"github.com/santhosh-tekuri/jsonschema/v6"

"github.com/pb33f/libopenapi-validator/config"
)

// ConfigureCompiler configures a JSON Schema compiler with the desired behavior.
func ConfigureCompiler(c *jsonschema.Compiler, o *config.ValidationOptions) {
// nil is the default so this is OK.
c.UseRegexpEngine(o.RegexEngine)

// Enable Format assertions if required.
if o.FormatAssertions {
c.AssertFormat()
}

Check warning on line 20 in helpers/schema_compiler.go

View check run for this annotation

Codecov / codecov/patch

helpers/schema_compiler.go#L13-L20

Added lines #L13 - L20 were not covered by tests

// Content Assertions
if o.ContentAssertions {
c.AssertContent()
}

Check warning on line 25 in helpers/schema_compiler.go

View check run for this annotation

Codecov / codecov/patch

helpers/schema_compiler.go#L23-L25

Added lines #L23 - L25 were not covered by tests
}

// NewCompilerWithOptions mints a new JSON schema compiler with custom configuration.
func NewCompilerWithOptions(o *config.ValidationOptions) *jsonschema.Compiler {
// Build it
c := jsonschema.NewCompiler()

// Configure it
ConfigureCompiler(c, o)

// Return it
return c

Check warning on line 37 in helpers/schema_compiler.go

View check run for this annotation

Codecov / codecov/patch

helpers/schema_compiler.go#L29-L37

Added lines #L29 - L37 were not covered by tests
}

// NewCompiledSchema establishes a programmatic representation of a JSON Schema document that is used for validation.
func NewCompiledSchema(name string, jsonSchema []byte, o *config.ValidationOptions) *jsonschema.Schema {
// Establish a compiler with the desired configuration
compiler := NewCompilerWithOptions(o)
compiler.UseLoader(NewCompilerLoader())

// Decode the JSON Schema into a JSON blob.
decodedSchema, _ := jsonschema.UnmarshalJSON(bytes.NewReader(jsonSchema))
_ = compiler.AddResource(fmt.Sprintf("%s.json", name), decodedSchema)

// Try to compile it.
jsch, _ := compiler.Compile(fmt.Sprintf("%s.json", name))

// Done.
return jsch

Check warning on line 54 in helpers/schema_compiler.go

View check run for this annotation

Codecov / codecov/patch

helpers/schema_compiler.go#L41-L54

Added lines #L41 - L54 were not covered by tests
}
2 changes: 2 additions & 0 deletions parameters/path_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func (v *paramValidator) ValidatePathParamsWithPathItem(request *http.Request, p
p.Name,
helpers.ParameterValidation,
helpers.ParameterValidationPath,
v.options,
)...)

case helpers.Integer, helpers.Number:
Expand All @@ -161,6 +162,7 @@ func (v *paramValidator) ValidatePathParamsWithPathItem(request *http.Request, p
p.Name,
helpers.ParameterValidation,
helpers.ParameterValidationPath,
v.options,
)...)

case helpers.Boolean:
Expand Down
1 change: 1 addition & 0 deletions parameters/query_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,5 +252,6 @@ func (v *paramValidator) validateSimpleParam(sch *base.Schema, rawParam string,
parameter.Name,
helpers.ParameterValidation,
helpers.ParameterValidationQuery,
v.options,
)
}
105 changes: 105 additions & 0 deletions parameters/query_parameters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/pb33f/libopenapi-validator/config"
"github.com/pb33f/libopenapi-validator/paths"
)

Expand Down Expand Up @@ -669,6 +670,110 @@ paths:
assert.Len(t, errors, 0)
}

func TestNewValidator_QueryParamValidDateFormat(t *testing.T) {
spec := `openapi: 3.1.0
paths:
/a/fishy/on/a/dishy:
get:
parameters:
- name: fishy
in: query
required: true
schema:
type: string
format: date`

doc, _ := libopenapi.NewDocument([]byte(spec))

m, _ := doc.BuildV3Model()

v := NewParameterValidator(&m.Model, config.WithFormatAssertions())

request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=2024-12-25", nil)

valid, errors := v.ValidateQueryParams(request)
assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestNewValidator_QueryParamInvalidDateFormat(t *testing.T) {
spec := `openapi: 3.1.0
paths:
/a/fishy/on/a/dishy:
get:
parameters:
- name: fishy
in: query
required: true
schema:
type: string
format: date`

doc, _ := libopenapi.NewDocument([]byte(spec))

m, _ := doc.BuildV3Model()

v := NewParameterValidator(&m.Model, config.WithFormatAssertions())

request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=12/25/2024", nil)

valid, errors := v.ValidateQueryParams(request)
assert.False(t, valid)
assert.Len(t, errors, 1)
}

func TestNewValidator_QueryParamValidDateTimeFormat(t *testing.T) {
spec := `openapi: 3.1.0
paths:
/a/fishy/on/a/dishy:
get:
parameters:
- name: fishy
in: query
required: true
schema:
type: string
format: date-time`

doc, _ := libopenapi.NewDocument([]byte(spec))

m, _ := doc.BuildV3Model()

v := NewParameterValidator(&m.Model, config.WithFormatAssertions())

request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=2024-12-25T13:42:42Z", nil)

valid, errors := v.ValidateQueryParams(request)
assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestNewValidator_QueryParamInvalidDateTimeFormat(t *testing.T) {
spec := `openapi: 3.1.0
paths:
/a/fishy/on/a/dishy:
get:
parameters:
- name: fishy
in: query
required: true
schema:
type: string
format: date-time`

doc, _ := libopenapi.NewDocument([]byte(spec))

m, _ := doc.BuildV3Model()

v := NewParameterValidator(&m.Model, config.WithFormatAssertions())

request, _ := http.NewRequest(http.MethodGet, "https://things.com/a/fishy/on/a/dishy?fishy=2024-12-25", nil)

valid, errors := v.ValidateQueryParams(request)
assert.False(t, valid)
assert.Len(t, errors, 1)
}

func TestNewValidator_QueryParamValidTypeArrayString(t *testing.T) {
spec := `openapi: 3.1.0
paths:
Expand Down
14 changes: 3 additions & 11 deletions parameters/validate_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

stdError "errors"

"github.com/pb33f/libopenapi-validator/config"
"github.com/pb33f/libopenapi-validator/errors"
"github.com/pb33f/libopenapi-validator/helpers"
)
Expand All @@ -30,8 +31,9 @@ func ValidateSingleParameterSchema(
name string,
validationType string,
subValType string,
o *config.ValidationOptions,
) (validationErrors []*errors.ValidationError) {
jsch := compileSchema(name, buildJsonRender(schema))
jsch := helpers.NewCompiledSchema(name, buildJsonRender(schema), o)

scErrs := jsch.Validate(rawObject)
var werras *jsonschema.ValidationError
Expand All @@ -41,16 +43,6 @@ func ValidateSingleParameterSchema(
return validationErrors
}

// compileSchema create a new json schema compiler and add the schema to it.
func compileSchema(name string, jsonSchema []byte) *jsonschema.Schema {
compiler := jsonschema.NewCompiler()
compiler.UseLoader(helpers.NewCompilerLoader())
decodedSchema, _ := jsonschema.UnmarshalJSON(strings.NewReader(string(jsonSchema))) // decode the schema into a json blob
_ = compiler.AddResource(fmt.Sprintf("%s.json", name), decodedSchema)
jsch, _ := compiler.Compile(fmt.Sprintf("%s.json", name))
return jsch
}

// buildJsonRender build a JSON render of the schema.
func buildJsonRender(schema *base.Schema) []byte {
renderedSchema, _ := schema.Render()
Expand Down
Loading