Skip to content

Commit

Permalink
Add variadic options to Validate method (#692)
Browse files Browse the repository at this point in the history
  • Loading branch information
fenollp committed Dec 3, 2022
1 parent 7f75486 commit b4b41f3
Show file tree
Hide file tree
Showing 31 changed files with 284 additions and 154 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/go.yml
Expand Up @@ -36,13 +36,13 @@ jobs:
- run: echo ${{ steps.go-cache-paths.outputs.go-mod }}

- name: Go Build Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-${{ matrix.go }}-build-${{ hashFiles('**/go.sum') }}

- name: Go Mod Cache (go>=1.15)
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-go-${{ matrix.go }}-mod-${{ hashFiles('**/go.sum') }}
Expand All @@ -61,6 +61,7 @@ jobs:
- run: go fmt ./...
- run: git --no-pager diff --exit-code

- run: go test ./...
- if: runner.os == 'Linux'
run: go test -count=10 ./...
env:
Expand Down Expand Up @@ -116,7 +117,7 @@ jobs:
fi
# Ensure impl Validate()
if ! git grep -InE 'func [(].+Schema[)] Validate[(]ctx context.Context[)].+error.+[{]'; then
if ! git grep -InE 'func [(].+'"$ty"'[)] Validate[(]ctx context.Context, opts [.][.][.]ValidationOption[)].+error.+[{]'; then
echo "OAI type $ty does not implement Validate()" && exit 1
fi
Expand Down
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -196,6 +196,10 @@ func arrayUniqueItemsChecker(items []interface{}) bool {

## Sub-v0 breaking API changes

### v0.111.0
* Changed `func (*_) Validate(ctx context.Context) error` to `func (*_) Validate(ctx context.Context, opts ...ValidationOption) error`.
* `openapi3.WithValidationOptions(ctx context.Context, opts *ValidationOptions) context.Context` prototype changed to `openapi3.WithValidationOptions(ctx context.Context, opts ...ValidationOption) context.Context`.

### v0.101.0
* `openapi3.SchemaFormatValidationDisabled` has been removed in favour of an option `openapi3.EnableSchemaFormatValidation()` passed to `openapi3.T.Validate`. The default behaviour is also now to not validate formats, as the OpenAPI spec mentions the `format` is an open value.

Expand Down
4 changes: 3 additions & 1 deletion openapi3/callback.go
Expand Up @@ -30,7 +30,9 @@ func (c Callbacks) JSONLookup(token string) (interface{}, error) {
type Callback map[string]*PathItem

// Validate returns an error if Callback does not comply with the OpenAPI spec.
func (callback Callback) Validate(ctx context.Context) error {
func (callback Callback) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

keys := make([]string, 0, len(callback))
for key := range callback {
keys = append(keys, key)
Expand Down
4 changes: 3 additions & 1 deletion openapi3/components.go
Expand Up @@ -40,7 +40,9 @@ func (components *Components) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if Components does not comply with the OpenAPI spec.
func (components *Components) Validate(ctx context.Context) (err error) {
func (components *Components) Validate(ctx context.Context, opts ...ValidationOption) (err error) {
ctx = WithValidationOptions(ctx, opts...)

schemas := make([]string, 0, len(components.Schemas))
for name := range components.Schemas {
schemas = append(schemas, name)
Expand Down
4 changes: 3 additions & 1 deletion openapi3/content.go
Expand Up @@ -106,7 +106,9 @@ func (content Content) Get(mime string) *MediaType {
}

// Validate returns an error if Content does not comply with the OpenAPI spec.
func (content Content) Validate(ctx context.Context) error {
func (content Content) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

keys := make([]string, 0, len(content))
for key := range content {
keys = append(keys, key)
Expand Down
4 changes: 3 additions & 1 deletion openapi3/discriminator.go
Expand Up @@ -26,6 +26,8 @@ func (discriminator *Discriminator) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if Discriminator does not comply with the OpenAPI spec.
func (discriminator *Discriminator) Validate(ctx context.Context) error {
func (discriminator *Discriminator) Validate(ctx context.Context, opts ...ValidationOption) error {
// ctx = WithValidationOptions(ctx, opts...)

return nil
}
4 changes: 3 additions & 1 deletion openapi3/encoding.go
Expand Up @@ -66,7 +66,9 @@ func (encoding *Encoding) SerializationMethod() *SerializationMethod {
}

// Validate returns an error if Encoding does not comply with the OpenAPI spec.
func (encoding *Encoding) Validate(ctx context.Context) error {
func (encoding *Encoding) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

if encoding == nil {
return nil
}
Expand Down
4 changes: 3 additions & 1 deletion openapi3/example.go
Expand Up @@ -55,7 +55,9 @@ func (example *Example) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if Example does not comply with the OpenAPI spec.
func (example *Example) Validate(ctx context.Context) error {
func (example *Example) Validate(ctx context.Context, opts ...ValidationOption) error {
// ctx = WithValidationOptions(ctx, opts...)

if example.Value != nil && example.ExternalValue != "" {
return errors.New("value and externalValue are mutually exclusive")
}
Expand Down
8 changes: 3 additions & 5 deletions openapi3/example_validation_test.go
Expand Up @@ -221,8 +221,6 @@ func TestExamplesSchemaValidation(t *testing.T) {
t.Parallel()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
loader := NewLoader()

spec := bytes.Buffer{}
spec.WriteString(`
openapi: 3.0.3
Expand Down Expand Up @@ -339,13 +337,14 @@ components:
`)
spec.WriteString(tc.componentExamples)

loader := NewLoader()
doc, err := loader.LoadFromData(spec.Bytes())
require.NoError(t, err)

if testOption.disableExamplesValidation {
err = doc.Validate(loader.Context, DisableExamplesValidation())
} else {
err = doc.Validate(loader.Context)
err = doc.Validate(loader.Context, EnableExamplesValidation())
}

if tc.errContains != "" && !testOption.disableExamplesValidation {
Expand Down Expand Up @@ -436,8 +435,6 @@ func TestExampleObjectValidation(t *testing.T) {
t.Parallel()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
loader := NewLoader()

spec := bytes.Buffer{}
spec.WriteString(`
openapi: 3.0.3
Expand Down Expand Up @@ -506,6 +503,7 @@ components:
`)
spec.WriteString(tc.componentExamples)

loader := NewLoader()
doc, err := loader.LoadFromData(spec.Bytes())
require.NoError(t, err)

Expand Down
4 changes: 3 additions & 1 deletion openapi3/external_docs.go
Expand Up @@ -29,7 +29,9 @@ func (e *ExternalDocs) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if ExternalDocs does not comply with the OpenAPI spec.
func (e *ExternalDocs) Validate(ctx context.Context) error {
func (e *ExternalDocs) Validate(ctx context.Context, opts ...ValidationOption) error {
// ctx = WithValidationOptions(ctx, opts...)

if e.URL == "" {
return errors.New("url is required")
}
Expand Down
4 changes: 3 additions & 1 deletion openapi3/header.go
Expand Up @@ -54,7 +54,9 @@ func (header *Header) SerializationMethod() (*SerializationMethod, error) {
}

// Validate returns an error if Header does not comply with the OpenAPI spec.
func (header *Header) Validate(ctx context.Context) error {
func (header *Header) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

if header.Name != "" {
return errors.New("header 'name' MUST NOT be specified, it is given in the corresponding headers map")
}
Expand Down
12 changes: 9 additions & 3 deletions openapi3/info.go
Expand Up @@ -31,7 +31,9 @@ func (info *Info) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if Info does not comply with the OpenAPI spec.
func (info *Info) Validate(ctx context.Context) error {
func (info *Info) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

if contact := info.Contact; contact != nil {
if err := contact.Validate(ctx); err != nil {
return err
Expand Down Expand Up @@ -76,7 +78,9 @@ func (contact *Contact) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if Contact does not comply with the OpenAPI spec.
func (contact *Contact) Validate(ctx context.Context) error {
func (contact *Contact) Validate(ctx context.Context, opts ...ValidationOption) error {
// ctx = WithValidationOptions(ctx, opts...)

return nil
}

Expand All @@ -100,7 +104,9 @@ func (license *License) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if License does not comply with the OpenAPI spec.
func (license *License) Validate(ctx context.Context) error {
func (license *License) Validate(ctx context.Context, opts ...ValidationOption) error {
// ctx = WithValidationOptions(ctx, opts...)

if license.Name == "" {
return errors.New("value of license name must be a non-empty string")
}
Expand Down
4 changes: 3 additions & 1 deletion openapi3/link.go
Expand Up @@ -51,7 +51,9 @@ func (link *Link) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if Link does not comply with the OpenAPI spec.
func (link *Link) Validate(ctx context.Context) error {
func (link *Link) Validate(ctx context.Context, opts ...ValidationOption) error {
// ctx = WithValidationOptions(ctx, opts...)

if link.OperationID == "" && link.OperationRef == "" {
return errors.New("missing operationId or operationRef on link")
}
Expand Down
4 changes: 3 additions & 1 deletion openapi3/media_type.go
Expand Up @@ -75,7 +75,9 @@ func (mediaType *MediaType) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if MediaType does not comply with the OpenAPI spec.
func (mediaType *MediaType) Validate(ctx context.Context) error {
func (mediaType *MediaType) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

if mediaType == nil {
return nil
}
Expand Down
6 changes: 1 addition & 5 deletions openapi3/openapi3.go
Expand Up @@ -56,11 +56,7 @@ func (doc *T) AddServer(server *Server) {
// Validate returns an error if T does not comply with the OpenAPI spec.
// Validations Options can be provided to modify the validation behavior.
func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error {
validationOpts := &ValidationOptions{}
for _, opt := range opts {
opt(validationOpts)
}
ctx = WithValidationOptions(ctx, validationOpts)
ctx = WithValidationOptions(ctx, opts...)

if doc.OpenAPI == "" {
return errors.New("value of openapi must be a non-empty string")
Expand Down
4 changes: 3 additions & 1 deletion openapi3/operation.go
Expand Up @@ -127,7 +127,9 @@ func (operation *Operation) AddResponse(status int, response *Response) {
}

// Validate returns an error if Operation does not comply with the OpenAPI spec.
func (operation *Operation) Validate(ctx context.Context) error {
func (operation *Operation) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

if v := operation.Parameters; v != nil {
if err := v.Validate(ctx); err != nil {
return err
Expand Down
8 changes: 6 additions & 2 deletions openapi3/parameter.go
Expand Up @@ -69,7 +69,9 @@ func (parameters Parameters) GetByInAndName(in string, name string) *Parameter {
}

// Validate returns an error if Parameters does not comply with the OpenAPI spec.
func (parameters Parameters) Validate(ctx context.Context) error {
func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

dupes := make(map[string]struct{})
for _, parameterRef := range parameters {
if v := parameterRef.Value; v != nil {
Expand Down Expand Up @@ -247,7 +249,9 @@ func (parameter *Parameter) SerializationMethod() (*SerializationMethod, error)
}

// Validate returns an error if Parameter does not comply with the OpenAPI spec.
func (parameter *Parameter) Validate(ctx context.Context) error {
func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

if parameter.Name == "" {
return errors.New("parameter name can't be blank")
}
Expand Down
4 changes: 3 additions & 1 deletion openapi3/path_item.go
Expand Up @@ -123,7 +123,9 @@ func (pathItem *PathItem) SetOperation(method string, operation *Operation) {
}

// Validate returns an error if PathItem does not comply with the OpenAPI spec.
func (pathItem *PathItem) Validate(ctx context.Context) error {
func (pathItem *PathItem) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

operations := pathItem.Operations()

methods := make([]string, 0, len(operations))
Expand Down
4 changes: 3 additions & 1 deletion openapi3/paths.go
Expand Up @@ -12,7 +12,9 @@ import (
type Paths map[string]*PathItem

// Validate returns an error if Paths does not comply with the OpenAPI spec.
func (paths Paths) Validate(ctx context.Context) error {
func (paths Paths) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

normalizedPaths := make(map[string]string, len(paths))

keys := make([]string, 0, len(paths))
Expand Down
27 changes: 18 additions & 9 deletions openapi3/refs.go
Expand Up @@ -39,7 +39,8 @@ func (value *CallbackRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if CallbackRef does not comply with the OpenAPI spec.
func (value *CallbackRef) Validate(ctx context.Context) error {
func (value *CallbackRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -81,7 +82,8 @@ func (value *ExampleRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if ExampleRef does not comply with the OpenAPI spec.
func (value *ExampleRef) Validate(ctx context.Context) error {
func (value *ExampleRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -123,7 +125,8 @@ func (value *HeaderRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if HeaderRef does not comply with the OpenAPI spec.
func (value *HeaderRef) Validate(ctx context.Context) error {
func (value *HeaderRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -163,7 +166,8 @@ func (value *LinkRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if LinkRef does not comply with the OpenAPI spec.
func (value *LinkRef) Validate(ctx context.Context) error {
func (value *LinkRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -195,7 +199,8 @@ func (value *ParameterRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if ParameterRef does not comply with the OpenAPI spec.
func (value *ParameterRef) Validate(ctx context.Context) error {
func (value *ParameterRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -237,7 +242,8 @@ func (value *ResponseRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if ResponseRef does not comply with the OpenAPI spec.
func (value *ResponseRef) Validate(ctx context.Context) error {
func (value *ResponseRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -279,7 +285,8 @@ func (value *RequestBodyRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if RequestBodyRef does not comply with the OpenAPI spec.
func (value *RequestBodyRef) Validate(ctx context.Context) error {
func (value *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -328,7 +335,8 @@ func (value *SchemaRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if SchemaRef does not comply with the OpenAPI spec.
func (value *SchemaRef) Validate(ctx context.Context) error {
func (value *SchemaRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down Expand Up @@ -370,7 +378,8 @@ func (value *SecuritySchemeRef) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if SecuritySchemeRef does not comply with the OpenAPI spec.
func (value *SecuritySchemeRef) Validate(ctx context.Context) error {
func (value *SecuritySchemeRef) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
if v := value.Value; v != nil {
return v.Validate(ctx)
}
Expand Down
4 changes: 3 additions & 1 deletion openapi3/request_body.go
Expand Up @@ -105,7 +105,9 @@ func (requestBody *RequestBody) UnmarshalJSON(data []byte) error {
}

// Validate returns an error if RequestBody does not comply with the OpenAPI spec.
func (requestBody *RequestBody) Validate(ctx context.Context) error {
func (requestBody *RequestBody) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

if requestBody.Content == nil {
return errors.New("content of the request body is required")
}
Expand Down

0 comments on commit b4b41f3

Please sign in to comment.