Skip to content

Commit

Permalink
Addressed Review Comments
Browse files Browse the repository at this point in the history
- Removed .gitignore
- Fixed import ordering
- Adopted 'Options pattern' for configuration.
  • Loading branch information
JemDay committed Dec 11, 2024
1 parent c99382e commit f96021d
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 67 deletions.
1 change: 0 additions & 1 deletion .gitignore

This file was deleted.

25 changes: 15 additions & 10 deletions parameters/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
package parameters

import (
"github.com/santhosh-tekuri/jsonschema/v6"
"net/http"

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

v3 "github.com/pb33f/libopenapi/datamodel/high/v3"

"github.com/pb33f/libopenapi-validator/errors"
Expand Down Expand Up @@ -66,23 +67,27 @@ type ParameterValidator interface {
ValidateSecurityWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError)
}

type Config struct {
RegexEngine jsonschema.RegexpEngine
type Option func(validator *paramValidator)

func WithRegexEngine(engine jsonschema.RegexpEngine) Option {
return func(pv *paramValidator) {
pv.regexEngine = engine
}

Check warning on line 75 in parameters/parameters.go

View check run for this annotation

Codecov / codecov/patch

parameters/parameters.go#L72-L75

Added lines #L72 - L75 were not covered by tests
}

// NewParameterValidator will create a new ParameterValidator from an OpenAPI 3+ document
func NewParameterValidator(document *v3.Document, cfg ...Config) ParameterValidator {
func NewParameterValidator(document *v3.Document, opts ...Option) ParameterValidator {

config := Config{}
pv := paramValidator{document: document}

if len(cfg) > 0 {
config = cfg[0]
for _, opt := range opts {
opt(&pv)
}

Check warning on line 85 in parameters/parameters.go

View check run for this annotation

Codecov / codecov/patch

parameters/parameters.go#L84-L85

Added lines #L84 - L85 were not covered by tests

return &paramValidator{config, document}
return &pv
}

type paramValidator struct {
Config
document *v3.Document
regexEngine jsonschema.RegexpEngine
document *v3.Document
}
29 changes: 19 additions & 10 deletions requests/request_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
package requests

import (
"github.com/santhosh-tekuri/jsonschema/v6"
"net/http"
"sync"

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

Check failure on line 10 in requests/request_body.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gci`-ed with --skip-generated -s standard -s default -s localmodule -s blank -s dot -s alias (gci)

"github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/pb33f/libopenapi/datamodel/high/v3"

Expand All @@ -30,20 +31,28 @@ type RequestBodyValidator interface {
ValidateRequestBodyWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError)
}

type Config struct {
RegexEngine jsonschema.RegexpEngine
type configOptions struct {
regexEngine jsonschema.RegexpEngine
}

// NewRequestBodyValidator will create a new RequestBodyValidator from an OpenAPI 3+ document
func NewRequestBodyValidator(document *v3.Document, cfg ...Config) RequestBodyValidator {
type Option func(options *configOptions)

config := Config{} // Default
func WithRegexEngine(engine jsonschema.RegexpEngine) Option {
return func(rbv *configOptions) {
rbv.regexEngine = engine
}
}

// NewRequestBodyValidator will create a new RequestBodyValidator from an OpenAPI 3+ document
func NewRequestBodyValidator(document *v3.Document, opt ...Option) RequestBodyValidator {

if len(cfg) > 0 {
config = cfg[0]
cfg := configOptions{} // Default Options
for _, o := range opt {
o(&cfg)
}

Check warning on line 52 in requests/request_body.go

View check run for this annotation

Codecov / codecov/patch

requests/request_body.go#L51-L52

Added lines #L51 - L52 were not covered by tests

return &requestBodyValidator{Config: config, document: document, schemaCache: &sync.Map{}}
return &requestBodyValidator{configOptions: cfg, document: document, schemaCache: &sync.Map{}}

}

type schemaCache struct {
Expand All @@ -53,7 +62,7 @@ type schemaCache struct {
}

type requestBodyValidator struct {
Config
configOptions
document *v3.Document
schemaCache *sync.Map
}
2 changes: 1 addition & 1 deletion requests/validate_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (v *requestBodyValidator) ValidateRequestBodyWithPathItem(request *http.Req
}

// render the schema, to be used for validation
validationSucceeded, validationErrors := ValidateRequestSchema(request, schema, renderedInline, renderedJSON, v.Config)
validationSucceeded, validationErrors := ValidateRequestSchema(request, schema, renderedInline, renderedJSON, WithRegexEngine(v.regexEngine))

errors.PopulateValidationErrors(validationErrors, request, pathValue)

Expand Down
10 changes: 5 additions & 5 deletions requests/validate_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ func ValidateRequestSchema(
schema *base.Schema,
renderedSchema,
jsonSchema []byte,
cfg ...Config,
opts ...Option,
) (bool, []*errors.ValidationError) {

config := Config{} // Default
if len(cfg) > 0 {
config = cfg[0]
config := configOptions{}
for _, opt := range opts {
opt(&config)
}

var validationErrors []*errors.ValidationError
Expand Down Expand Up @@ -114,7 +114,7 @@ func ValidateRequestSchema(
}

compiler := jsonschema.NewCompiler()
compiler.UseRegexpEngine(config.RegexEngine) // Ensure any configured regex engine is used.
compiler.UseRegexpEngine(config.regexEngine) // Ensure any configured regex engine is used.
compiler.UseLoader(helpers.NewCompilerLoader())
decodedSchema, _ := jsonschema.UnmarshalJSON(strings.NewReader(string(jsonSchema)))
_ = compiler.AddResource("requestBody.json", decodedSchema)
Expand Down
25 changes: 17 additions & 8 deletions responses/response_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,29 @@ type ResponseBodyValidator interface {
ValidateResponseBodyWithPathItem(request *http.Request, response *http.Response, pathItem *v3.PathItem, pathFound string) (bool, []*errors.ValidationError)
}

type Config struct {
RegexEngine jsonschema.RegexpEngine
type Option func(validator *configOptions)

func WithRegexEngine(engine jsonschema.RegexpEngine) Option {
return func(v *configOptions) {
v.regexEngine = engine
}
}

type configOptions struct {
regexEngine jsonschema.RegexpEngine
}

// NewResponseBodyValidator will create a new ResponseBodyValidator from an OpenAPI 3+ document
func NewResponseBodyValidator(document *v3.Document, cfg ...Config) ResponseBodyValidator {
func NewResponseBodyValidator(document *v3.Document, opts ...Option) ResponseBodyValidator {

config := Config{} // Default
cfg := configOptions{} // Default Config

if len(cfg) > 0 {
config = cfg[0]
for _, opt := range opts {
opt(&cfg)
}

Check warning on line 53 in responses/response_body.go

View check run for this annotation

Codecov / codecov/patch

responses/response_body.go#L52-L53

Added lines #L52 - L53 were not covered by tests

return &responseBodyValidator{Config: config, document: document, schemaCache: &sync.Map{}}
return &responseBodyValidator{configOptions: cfg, document: document, schemaCache: &sync.Map{}}

}

type schemaCache struct {
Expand All @@ -54,7 +63,7 @@ type schemaCache struct {
}

type responseBodyValidator struct {
Config
configOptions
document *v3.Document
schemaCache *sync.Map
}
2 changes: 1 addition & 1 deletion responses/validate_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (v *responseBodyValidator) checkResponseSchema(
}

// render the schema, to be used for validation
valid, vErrs := ValidateResponseSchema(request, response, schema, renderedInline, renderedJSON, v.Config)
valid, vErrs := ValidateResponseSchema(request, response, schema, renderedInline, renderedJSON, WithRegexEngine(v.regexEngine))
if !valid {
validationErrors = append(validationErrors, vErrs...)
}
Expand Down
12 changes: 6 additions & 6 deletions responses/validate_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ func ValidateResponseSchema(
schema *base.Schema,
renderedSchema,
jsonSchema []byte,
cfg ...Config,
cfg ...Option,
) (bool, []*errors.ValidationError) {

config := Config{} // A Default

if len(cfg) > 0 {
config = cfg[0]
// Apply config options
config := configOptions{} // Default config
for _, opt := range cfg {
opt(&config)
}

var validationErrors []*errors.ValidationError
Expand Down Expand Up @@ -134,7 +134,7 @@ func ValidateResponseSchema(

// create a new jsonschema compiler and add in the rendered JSON schema.
compiler := jsonschema.NewCompiler()
compiler.UseRegexpEngine(config.RegexEngine)
compiler.UseRegexpEngine(config.regexEngine)
compiler.UseLoader(helpers.NewCompilerLoader())
fName := fmt.Sprintf("%s.json", helpers.ResponseBodyValidation)
decodedSchema, _ := jsonschema.UnmarshalJSON(strings.NewReader(string(jsonSchema)))
Expand Down
39 changes: 18 additions & 21 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,48 +63,44 @@ type Validator interface {
GetResponseBodyValidator() responses.ResponseBodyValidator
}

// Configuration Holds any Validator configuration overrides.
type Configuration struct {
// Use this regex engine in place of the standard Go (RE2) pattern processor
RegexEngine jsonschema.RegexpEngine
type Option func(*validator)

func WithRegexEngine(engine jsonschema.RegexpEngine) Option {
return func(v *validator) {
v.regexEngine = engine
}
}

// NewValidator will create a new Validator from an OpenAPI 3+ document
func NewValidator(document libopenapi.Document, config ...Configuration) (Validator, []error) {
func NewValidator(document libopenapi.Document, opts ...Option) (Validator, []error) {
m, errs := document.BuildV3Model()
if errs != nil {
return nil, errs
}
v := NewValidatorFromV3Model(&m.Model, config...)
v := NewValidatorFromV3Model(&m.Model, opts...)
v.(*validator).document = document
return v, nil
}

// NewValidatorFromV3Model will create a new Validator from an OpenAPI Model
func NewValidatorFromV3Model(m *v3.Document, config ...Configuration) Validator {
func NewValidatorFromV3Model(m *v3.Document, opts ...Option) Validator {

// Assume a default configuration
cfg := Configuration{}
v := &validator{v3Model: m}

if len(config) > 0 {
cfg = config[0]
for _, opt := range opts {
opt(v)
}

// create a new parameter validator
paramValidator := parameters.NewParameterValidator(m, parameters.Config{RegexEngine: cfg.RegexEngine})
v.paramValidator = parameters.NewParameterValidator(m, parameters.WithRegexEngine(v.regexEngine))

// create a new request body validator
reqBodyValidator := requests.NewRequestBodyValidator(m, requests.Config{RegexEngine: cfg.RegexEngine})
// create aq new request body validator
v.requestValidator = requests.NewRequestBodyValidator(m)

// create a response body validator
respBodyValidator := responses.NewResponseBodyValidator(m, responses.Config{RegexEngine: cfg.RegexEngine})
v.responseValidator = responses.NewResponseBodyValidator(m, responses.WithRegexEngine(v.regexEngine))

return &validator{
v3Model: m,
requestValidator: reqBodyValidator,
responseValidator: respBodyValidator,
paramValidator: paramValidator,
}
return v
}

func (v *validator) GetParameterValidator() parameters.ParameterValidator {
Expand Down Expand Up @@ -325,6 +321,7 @@ type validator struct {
paramValidator parameters.ParameterValidator
requestValidator requests.RequestBodyValidator
responseValidator responses.ResponseBodyValidator
regexEngine jsonschema.RegexpEngine
}

func runValidation(control, doneChan chan struct{},
Expand Down
20 changes: 16 additions & 4 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package validator
import (
"bytes"
"encoding/json"
"github.com/santhosh-tekuri/jsonschema/v6"
"net/http"
"net/http/httptest"
"os"
Expand Down Expand Up @@ -136,13 +137,24 @@ func TestNewValidator_ValidateDocument(t *testing.T) {
assert.Len(t, errs, 0)
}

func TestNewValidator_WithConfig(t *testing.T) {
type alwaysMatchesRegex jsonschema.RegexpEngine

// This needs work.
validatorConfig := Configuration{}
func (dr *alwaysMatchesRegex) MatchString(s string) bool {
return true
}

func (dr *alwaysMatchesRegex) String() string {
return ""
}

func fakeRegexEngine(s string) (jsonschema.Regexp, error) {
return (*alwaysMatchesRegex)(nil), nil
}

func TestNewValidator_WithRegex(t *testing.T) {

doc, _ := libopenapi.NewDocument(petstoreBytes)
v, _ := NewValidator(doc, validatorConfig)
v, _ := NewValidator(doc, WithRegexEngine(fakeRegexEngine))
require.NotNil(t, v, "Failed to build validator")
valid, errs := v.ValidateDocument()
assert.True(t, valid)
Expand Down

0 comments on commit f96021d

Please sign in to comment.