Skip to content

Commit

Permalink
Merge pull request #875 from k1LoW/global-schema-registory
Browse files Browse the repository at this point in the history
Provide registry for OpenAPI3 documents to cache it.
  • Loading branch information
k2tzumi committed Apr 16, 2024
2 parents 583c63a + 0908407 commit ad54e1c
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 37 deletions.
2 changes: 1 addition & 1 deletion coverage.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (o *operator) collectCoverage(ctx context.Context) (*Coverage, error) {
o.Debugf("%s does not have openapi3 spec document (%s)\n", name, o.bookPath)
continue
}
doc := *ov.doc
doc := *ov.doc.doc
v3m, errs := doc.BuildV3Model()
if len(errs) > 0 {
return nil, errors.Join(errs...)
Expand Down
82 changes: 62 additions & 20 deletions http_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package runn

import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -57,14 +59,27 @@ func newNopValidator() *nopValidator {
return &nopValidator{}
}

// globalOpenAPI3DocRegistory - global registory of OpenAPI3 documents.
var globalOpenAPI3DocRegistory = map[string]*openAPI3Doc{}

type openAPI3Doc struct {
doc *libopenapi.Document
validator *validator.Validator
}

type openAPI3Validator struct {
skipValidateRequest bool
skipValidateResponse bool
doc *libopenapi.Document
validator *validator.Validator
doc *openAPI3Doc
}

func newOpenAPI3Validator(c *httpRunnerConfig) (*openAPI3Validator, error) {
if c.OpenAPI3DocLocation == "" && c.openAPI3Doc == nil {
return nil, errors.New("cannot load openapi3 document")
}

var hash string

if c.OpenAPI3DocLocation != "" {
l := c.OpenAPI3DocLocation
var doc libopenapi.Document
Expand All @@ -83,6 +98,15 @@ func newOpenAPI3Validator(c *httpRunnerConfig) (*openAPI3Validator, error) {
if err != nil {
return nil, err
}
hash = hashBytes(b)
od, ok := globalOpenAPI3DocRegistory[hash]
if ok {
return &openAPI3Validator{
skipValidateRequest: c.SkipValidateRequest,
skipValidateResponse: c.SkipValidateResponse,
doc: od,
}, nil
}
doc, err = libopenapi.NewDocumentWithConfiguration(b, openAPIConfig)
if err != nil {
return nil, err
Expand All @@ -92,42 +116,55 @@ func newOpenAPI3Validator(c *httpRunnerConfig) (*openAPI3Validator, error) {
if err != nil {
return nil, err
}
hash = hashBytes(b)
od, ok := globalOpenAPI3DocRegistory[hash]
if ok {
return &openAPI3Validator{
skipValidateRequest: c.SkipValidateRequest,
skipValidateResponse: c.SkipValidateResponse,
doc: od,
}, nil
}
openAPIConfig.BasePath = filepath.Dir(l)
doc, err = libopenapi.NewDocumentWithConfiguration(b, openAPIConfig)
if err != nil {
return nil, err
}
}
v, errs := validator.NewValidator(doc)
if len(errs) > 0 {
return nil, errors.Join(errs...)
}
if _, errs := v.ValidateDocument(); len(errs) > 0 {
var err error
for _, e := range errs {
err = errors.Join(err, e)
}
return nil, err
}
c.openAPI3Doc = &doc
c.openAPI3Validator = &v
}
if c.openAPI3Doc == nil {
return nil, errors.New("cannot load openapi3 document")

v, errs := validator.NewValidator(*c.openAPI3Doc)
if len(errs) > 0 {
return nil, errors.Join(errs...)
}
if _, errs := v.ValidateDocument(); len(errs) > 0 {
var err error
for _, e := range errs {
err = errors.Join(err, e)
}
return nil, err
}

doc := &openAPI3Doc{
doc: c.openAPI3Doc,
validator: &v,
}

globalOpenAPI3DocRegistory[hash] = doc

return &openAPI3Validator{
skipValidateRequest: c.SkipValidateRequest,
skipValidateResponse: c.SkipValidateResponse,
doc: c.openAPI3Doc,
validator: c.openAPI3Validator,
doc: doc,
}, nil
}

func (v *openAPI3Validator) ValidateRequest(ctx context.Context, req *http.Request) error {
if v.skipValidateRequest {
return nil
}
vv := *v.validator
vv := *v.doc.validator
_, errs := vv.ValidateHttpRequest(req)
if len(errs) > 0 {
var err error
Expand All @@ -154,7 +191,7 @@ func (v *openAPI3Validator) ValidateResponse(ctx context.Context, req *http.Requ
if v.skipValidateResponse {
return nil
}
vv := *v.validator
vv := *v.doc.validator
_, errs := vv.ValidateHttpResponse(req, res)
if len(errs) > 0 {
var err error
Expand All @@ -181,6 +218,11 @@ func (v *openAPI3Validator) ValidateResponse(ctx context.Context, req *http.Requ
return nil
}

func hashBytes(b []byte) string {
sum := sha256.Sum256(b)
return hex.EncodeToString(sum[:])
}

// nullableTypeError returns whether the error is nullable type error or not.
func nullableError(e *verrors.ValidationError) bool {
if len(e.SchemaValidationErrors) > 0 {
Expand Down
23 changes: 7 additions & 16 deletions runner_option.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package runn

import (
"errors"
"fmt"
"strconv"
"strings"

"github.com/goccy/go-yaml"
"github.com/pb33f/libopenapi"
validator "github.com/pb33f/libopenapi-validator"
"github.com/pb33f/libopenapi/datamodel"
)

Expand All @@ -32,8 +30,7 @@ type httpRunnerConfig struct {
UseCookie *bool `yaml:"useCookie,omitempty"`
Trace traceConfig

openAPI3Doc *libopenapi.Document
openAPI3Validator *validator.Validator
openAPI3Doc *libopenapi.Document
}

type traceConfig struct {
Expand Down Expand Up @@ -133,23 +130,17 @@ func OpenApi3FromData(d []byte) httpRunnerOption {
// OpenAPI3FromData sets OpenAPI Document from data.
func OpenAPI3FromData(d []byte) httpRunnerOption {
return func(c *httpRunnerConfig) error {
hash := hashBytes(d)
od, ok := globalOpenAPI3DocRegistory[hash]
if ok {
c.openAPI3Doc = od.doc
return nil
}
doc, err := libopenapi.NewDocumentWithConfiguration(d, openAPIConfig)
if err != nil {
return err
}
v, errs := validator.NewValidator(doc)
if len(errs) > 0 {
return errors.Join(errs...)
}
if _, errs := v.ValidateDocument(); len(errs) > 0 {
var err error
for _, e := range errs {
err = errors.Join(err, e)
}
return err
}
c.openAPI3Doc = &doc
c.openAPI3Validator = &v
return nil
}
}
Expand Down

0 comments on commit ad54e1c

Please sign in to comment.