Repository: getkin/kin-openapi
Pull request #692 (2022-12-03 15:53:48)

Add variadic options to Validate method


Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index aa40035..d6950eb 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -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') }}
@@ -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:
@@ -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
 
diff --git a/README.md b/README.md
index c8431f8..c222649 100644
--- a/README.md
+++ b/README.md
@@ -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.
 
diff --git a/openapi3/callback.go b/openapi3/callback.go
index 1e47369..6246d6d 100644
--- a/openapi3/callback.go
+++ b/openapi3/callback.go
@@ -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)
diff --git a/openapi3/components.go b/openapi3/components.go
index 02ae458..16b39f3 100644
--- a/openapi3/components.go
+++ b/openapi3/components.go
@@ -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)
diff --git a/openapi3/content.go b/openapi3/content.go
index 9443250..8abd411 100644
--- a/openapi3/content.go
+++ b/openapi3/content.go
@@ -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)
diff --git a/openapi3/discriminator.go b/openapi3/discriminator.go
index 28a2148..8eb2960 100644
--- a/openapi3/discriminator.go
+++ b/openapi3/discriminator.go
@@ -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
 }
diff --git a/openapi3/encoding.go b/openapi3/encoding.go
index bc4985c..082d3f2 100644
--- a/openapi3/encoding.go
+++ b/openapi3/encoding.go
@@ -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
 	}
diff --git a/openapi3/example.go b/openapi3/example.go
index 561f09b..e75d4d3 100644
--- a/openapi3/example.go
+++ b/openapi3/example.go
@@ -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")
 	}
diff --git a/openapi3/external_docs.go b/openapi3/external_docs.go
index 75ae0d7..65ec2e8 100644
--- a/openapi3/external_docs.go
+++ b/openapi3/external_docs.go
@@ -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")
 	}
diff --git a/openapi3/header.go b/openapi3/header.go
index c71d3f2..aefaa06 100644
--- a/openapi3/header.go
+++ b/openapi3/header.go
@@ -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")
 	}
diff --git a/openapi3/info.go b/openapi3/info.go
index fa6593c..c67f73a 100644
--- a/openapi3/info.go
+++ b/openapi3/info.go
@@ -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
@@ -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
 }
 
@@ -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")
 	}
diff --git a/openapi3/link.go b/openapi3/link.go
index 3fb4d78..1040a04 100644
--- a/openapi3/link.go
+++ b/openapi3/link.go
@@ -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")
 	}
diff --git a/openapi3/media_type.go b/openapi3/media_type.go
index 1a9bb51..74c11b7 100644
--- a/openapi3/media_type.go
+++ b/openapi3/media_type.go
@@ -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
 	}
diff --git a/openapi3/openapi3.go b/openapi3/openapi3.go
index 510df09..714f280 100644
--- a/openapi3/openapi3.go
+++ b/openapi3/openapi3.go
@@ -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")
diff --git a/openapi3/operation.go b/openapi3/operation.go
index 3abc3c4..d877049 100644
--- a/openapi3/operation.go
+++ b/openapi3/operation.go
@@ -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
diff --git a/openapi3/parameter.go b/openapi3/parameter.go
index dc82a49..9124d92 100644
--- a/openapi3/parameter.go
+++ b/openapi3/parameter.go
@@ -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 {
@@ -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")
 	}
diff --git a/openapi3/path_item.go b/openapi3/path_item.go
index 28ee4b8..5cba0a8 100644
--- a/openapi3/path_item.go
+++ b/openapi3/path_item.go
@@ -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))
diff --git a/openapi3/paths.go b/openapi3/paths.go
index e3da7d0..2af59f2 100644
--- a/openapi3/paths.go
+++ b/openapi3/paths.go
@@ -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))
diff --git a/openapi3/refs.go b/openapi3/refs.go
index e85f37e..7311c9d 100644
--- a/openapi3/refs.go
+++ b/openapi3/refs.go
@@ -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)
 	}
@@ -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)
 	}
@@ -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)
 	}
@@ -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)
 	}
@@ -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)
 	}
@@ -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)
 	}
@@ -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)
 	}
@@ -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)
 	}
@@ -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)
 	}
diff --git a/openapi3/request_body.go b/openapi3/request_body.go
index 3e7c0d6..225c3d3 100644
--- a/openapi3/request_body.go
+++ b/openapi3/request_body.go
@@ -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")
 	}
diff --git a/openapi3/response.go b/openapi3/response.go
index 287e290..d2f907d 100644
--- a/openapi3/response.go
+++ b/openapi3/response.go
@@ -33,7 +33,9 @@ func (responses Responses) Get(status int) *ResponseRef {
 }
 
 // Validate returns an error if Responses does not comply with the OpenAPI spec.
-func (responses Responses) Validate(ctx context.Context) error {
+func (responses Responses) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if len(responses) == 0 {
 		return errors.New("the responses object MUST contain at least one response code")
 	}
@@ -111,7 +113,9 @@ func (response *Response) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Response does not comply with the OpenAPI spec.
-func (response *Response) Validate(ctx context.Context) error {
+func (response *Response) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if response.Description == nil {
 		return errors.New("a short description of the response is required")
 	}
diff --git a/openapi3/schema.go b/openapi3/schema.go
index d2cd31c..9f874d9 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -602,7 +602,8 @@ func (schema *Schema) IsEmpty() bool {
 }
 
 // Validate returns an error if Schema does not comply with the OpenAPI spec.
-func (schema *Schema) Validate(ctx context.Context) error {
+func (schema *Schema) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	return schema.validate(ctx, []*Schema{})
 }
 
diff --git a/openapi3/security_requirements.go b/openapi3/security_requirements.go
index 5929975..dcdad0c 100644
--- a/openapi3/security_requirements.go
+++ b/openapi3/security_requirements.go
@@ -16,7 +16,9 @@ func (srs *SecurityRequirements) With(securityRequirement SecurityRequirement) *
 }
 
 // Validate returns an error if SecurityRequirements does not comply with the OpenAPI spec.
-func (srs SecurityRequirements) Validate(ctx context.Context) error {
+func (srs SecurityRequirements) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	for _, security := range srs {
 		if err := security.Validate(ctx); err != nil {
 			return err
@@ -42,6 +44,8 @@ func (security SecurityRequirement) Authenticate(provider string, scopes ...stri
 }
 
 // Validate returns an error if SecurityRequirement does not comply with the OpenAPI spec.
-func (security *SecurityRequirement) Validate(ctx context.Context) error {
+func (security *SecurityRequirement) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	return nil
 }
diff --git a/openapi3/security_scheme.go b/openapi3/security_scheme.go
index 3797389..2b3235d 100644
--- a/openapi3/security_scheme.go
+++ b/openapi3/security_scheme.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"net/url"
 
 	"github.com/go-openapi/jsonpointer"
 
@@ -110,7 +111,9 @@ func (ss *SecurityScheme) WithBearerFormat(value string) *SecurityScheme {
 }
 
 // Validate returns an error if SecurityScheme does not comply with the OpenAPI spec.
-func (ss *SecurityScheme) Validate(ctx context.Context) error {
+func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	hasIn := false
 	hasBearerFormat := false
 	hasFlow := false
@@ -204,20 +207,30 @@ func (flows *OAuthFlows) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if OAuthFlows does not comply with the OpenAPI spec.
-func (flows *OAuthFlows) Validate(ctx context.Context) error {
+func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if v := flows.Implicit; v != nil {
-		return v.Validate(ctx, oAuthFlowTypeImplicit)
+		if err := v.validate(ctx, oAuthFlowTypeImplicit, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'implicit' is invalid: %w", err)
+		}
 	}
 	if v := flows.Password; v != nil {
-		return v.Validate(ctx, oAuthFlowTypePassword)
+		if err := v.validate(ctx, oAuthFlowTypePassword, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'password' is invalid: %w", err)
+		}
 	}
 	if v := flows.ClientCredentials; v != nil {
-		return v.Validate(ctx, oAuthFlowTypeClientCredentials)
+		if err := v.validate(ctx, oAuthFlowTypeClientCredentials, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'clientCredentials' is invalid: %w", err)
+		}
 	}
 	if v := flows.AuthorizationCode; v != nil {
-		return v.Validate(ctx, oAuthFlowAuthorizationCode)
+		if err := v.validate(ctx, oAuthFlowAuthorizationCode, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'authorizationCode' is invalid: %w", err)
+		}
 	}
-	return errors.New("no OAuth flow is defined")
+	return nil
 }
 
 // OAuthFlow is specified by OpenAPI/Swagger standard version 3.
@@ -241,20 +254,60 @@ func (flow *OAuthFlow) UnmarshalJSON(data []byte) error {
 	return jsoninfo.UnmarshalStrictStruct(data, flow)
 }
 
-// Validate returns an error if OAuthFlow does not comply with the OpenAPI spec.
-func (flow *OAuthFlow) Validate(ctx context.Context, typ oAuthFlowType) error {
-	if typ == oAuthFlowAuthorizationCode || typ == oAuthFlowTypeImplicit {
-		if v := flow.AuthorizationURL; v == "" {
-			return errors.New("an OAuth flow is missing 'authorizationUrl in authorizationCode or implicit '")
+// Validate returns an error if OAuthFlows does not comply with the OpenAPI spec.
+func (flow *OAuthFlow) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
+	if v := flow.RefreshURL; v != "" {
+		if _, err := url.Parse(v); err != nil {
+			return fmt.Errorf("field 'refreshUrl' is invalid: %w", err)
 		}
 	}
-	if typ != oAuthFlowTypeImplicit {
-		if v := flow.TokenURL; v == "" {
-			return errors.New("an OAuth flow is missing 'tokenUrl in not implicit'")
+
+	if v := flow.Scopes; len(v) == 0 {
+		return errors.New("field 'scopes' is empty or missing")
+	}
+
+	return nil
+}
+
+func (flow *OAuthFlow) validate(ctx context.Context, typ oAuthFlowType, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
+	typeIn := func(types ...oAuthFlowType) bool {
+		for _, ty := range types {
+			if ty == typ {
+				return true
+			}
 		}
+		return false
 	}
-	if v := flow.Scopes; v == nil {
-		return errors.New("an OAuth flow is missing 'scopes'")
+
+	if in := typeIn(oAuthFlowTypeImplicit, oAuthFlowAuthorizationCode); true {
+		switch {
+		case flow.AuthorizationURL == "" && in:
+			return errors.New("field 'authorizationUrl' is empty or missing")
+		case flow.AuthorizationURL != "" && !in:
+			return errors.New("field 'authorizationUrl' should not be set")
+		case flow.AuthorizationURL != "":
+			if _, err := url.Parse(flow.AuthorizationURL); err != nil {
+				return fmt.Errorf("field 'authorizationUrl' is invalid: %w", err)
+			}
+		}
 	}
-	return nil
+
+	if in := typeIn(oAuthFlowTypePassword, oAuthFlowTypeClientCredentials, oAuthFlowAuthorizationCode); true {
+		switch {
+		case flow.TokenURL == "" && in:
+			return errors.New("field 'tokenUrl' is empty or missing")
+		case flow.TokenURL != "" && !in:
+			return errors.New("field 'tokenUrl' should not be set")
+		case flow.TokenURL != "":
+			if _, err := url.Parse(flow.TokenURL); err != nil {
+				return fmt.Errorf("field 'tokenUrl' is invalid: %w", err)
+			}
+		}
+	}
+
+	return flow.Validate(ctx, opts...)
 }
diff --git a/openapi3/server.go b/openapi3/server.go
index 3f989d8..3047996 100644
--- a/openapi3/server.go
+++ b/openapi3/server.go
@@ -16,7 +16,9 @@ import (
 type Servers []*Server
 
 // Validate returns an error if Servers does not comply with the OpenAPI spec.
-func (servers Servers) Validate(ctx context.Context) error {
+func (servers Servers) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	for _, v := range servers {
 		if err := v.Validate(ctx); err != nil {
 			return err
@@ -163,7 +165,9 @@ func (server Server) MatchRawURL(input string) ([]string, string, bool) {
 }
 
 // Validate returns an error if Server does not comply with the OpenAPI spec.
-func (server *Server) Validate(ctx context.Context) (err error) {
+func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (err error) {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if server.URL == "" {
 		return errors.New("value of url must be a non-empty string")
 	}
@@ -215,7 +219,9 @@ func (serverVariable *ServerVariable) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if ServerVariable does not comply with the OpenAPI spec.
-func (serverVariable *ServerVariable) Validate(ctx context.Context) error {
+func (serverVariable *ServerVariable) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	if serverVariable.Default == "" {
 		data, err := serverVariable.MarshalJSON()
 		if err != nil {
diff --git a/openapi3/tag.go b/openapi3/tag.go
index b6c24c8..f151e50 100644
--- a/openapi3/tag.go
+++ b/openapi3/tag.go
@@ -20,7 +20,9 @@ func (tags Tags) Get(name string) *Tag {
 }
 
 // Validate returns an error if Tags does not comply with the OpenAPI spec.
-func (tags Tags) Validate(ctx context.Context) error {
+func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	for _, v := range tags {
 		if err := v.Validate(ctx); err != nil {
 			return err
@@ -50,7 +52,9 @@ func (t *Tag) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Tag does not comply with the OpenAPI spec.
-func (t *Tag) Validate(ctx context.Context) error {
+func (t *Tag) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if v := t.ExternalDocs; v != nil {
 		if err := v.Validate(ctx); err != nil {
 			return fmt.Errorf("invalid external docs: %w", err)
diff --git a/openapi3/validation_options.go b/openapi3/validation_options.go
index d890087..a74364d 100644
--- a/openapi3/validation_options.go
+++ b/openapi3/validation_options.go
@@ -16,12 +16,29 @@ type ValidationOptions struct {
 type validationOptionsKey struct{}
 
 // EnableSchemaFormatValidation makes Validate not return an error when validating documents that mention schema formats that are not defined by the OpenAPIv3 specification.
+// By default, schema format validation is disabled.
 func EnableSchemaFormatValidation() ValidationOption {
 	return func(options *ValidationOptions) {
 		options.SchemaFormatValidationEnabled = true
 	}
 }
 
+// DisableSchemaFormatValidation does the opposite of EnableSchemaFormatValidation.
+// By default, schema format validation is disabled.
+func DisableSchemaFormatValidation() ValidationOption {
+	return func(options *ValidationOptions) {
+		options.SchemaFormatValidationEnabled = false
+	}
+}
+
+// EnableSchemaPatternValidation does the opposite of DisableSchemaPatternValidation.
+// By default, schema pattern validation is enabled.
+func EnableSchemaPatternValidation() ValidationOption {
+	return func(options *ValidationOptions) {
+		options.SchemaPatternValidationDisabled = false
+	}
+}
+
 // DisableSchemaPatternValidation makes Validate not return an error when validating patterns that are not supported by the Go regexp engine.
 func DisableSchemaPatternValidation() ValidationOption {
 	return func(options *ValidationOptions) {
@@ -29,7 +46,16 @@ func DisableSchemaPatternValidation() ValidationOption {
 	}
 }
 
+// EnableExamplesValidation does the opposite of DisableExamplesValidation.
+// By default, all schema examples are validated.
+func EnableExamplesValidation() ValidationOption {
+	return func(options *ValidationOptions) {
+		options.ExamplesValidationDisabled = false
+	}
+}
+
 // DisableExamplesValidation disables all example schema validation.
+// By default, all schema examples are validated.
 func DisableExamplesValidation() ValidationOption {
 	return func(options *ValidationOptions) {
 		options.ExamplesValidationDisabled = true
@@ -37,7 +63,14 @@ func DisableExamplesValidation() ValidationOption {
 }
 
 // WithValidationOptions allows adding validation options to a context object that can be used when validationg any OpenAPI type.
-func WithValidationOptions(ctx context.Context, options *ValidationOptions) context.Context {
+func WithValidationOptions(ctx context.Context, opts ...ValidationOption) context.Context {
+	if len(opts) == 0 {
+		return ctx
+	}
+	options := &ValidationOptions{}
+	for _, opt := range opts {
+		opt(options)
+	}
 	return context.WithValue(ctx, validationOptionsKey{}, options)
 }
 
diff --git a/openapi3/xml.go b/openapi3/xml.go
index f1ab96b..4ed3d94 100644
--- a/openapi3/xml.go
+++ b/openapi3/xml.go
@@ -29,6 +29,8 @@ func (xml *XML) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if XML does not comply with the OpenAPI spec.
-func (xml *XML) Validate(ctx context.Context) error {
+func (xml *XML) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	return nil // TODO
 }


Test patch:
diff --git a/openapi3/example_validation_test.go b/openapi3/example_validation_test.go
index 79288c2..6ce7c0a 100644
--- a/openapi3/example_validation_test.go
+++ b/openapi3/example_validation_test.go
@@ -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
@@ -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 {
@@ -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
@@ -506,6 +503,7 @@ components:
 `)
 					spec.WriteString(tc.componentExamples)
 
+					loader := NewLoader()
 					doc, err := loader.LoadFromData(spec.Bytes())
 					require.NoError(t, err)
 
diff --git a/openapi3/schema_test.go b/openapi3/schema_test.go
index abec304..89971cf 100644
--- a/openapi3/schema_test.go
+++ b/openapi3/schema_test.go
@@ -1028,9 +1028,7 @@ func testType(t *testing.T, example schemaTypeExample) func(*testing.T) {
 		}
 		for _, typ := range example.AllInvalid {
 			schema := baseSchema.WithFormat(typ)
-			ctx := WithValidationOptions(context.Background(), &ValidationOptions{
-				SchemaFormatValidationEnabled: true,
-			})
+			ctx := WithValidationOptions(context.Background(), EnableSchemaFormatValidation())
 			err := schema.Validate(ctx)
 			require.Error(t, err)
 		}
diff --git a/openapi3/security_scheme_test.go b/openapi3/security_scheme_test.go
index cba0b84..5958c53 100644
--- a/openapi3/security_scheme_test.go
+++ b/openapi3/security_scheme_test.go
@@ -15,22 +15,18 @@ type securitySchemeExample struct {
 
 func TestSecuritySchemaExample(t *testing.T) {
 	for _, example := range securitySchemeExamples {
-		t.Run(example.title, testSecuritySchemaExample(t, example))
-	}
-}
-
-func testSecuritySchemaExample(t *testing.T, e securitySchemeExample) func(*testing.T) {
-	return func(t *testing.T) {
-		var err error
-		ss := &SecurityScheme{}
-		err = ss.UnmarshalJSON(e.raw)
-		require.NoError(t, err)
-		err = ss.Validate(context.Background())
-		if e.valid {
+		t.Run(example.title, func(t *testing.T) {
+			ss := &SecurityScheme{}
+			err := ss.UnmarshalJSON(example.raw)
 			require.NoError(t, err)
-		} else {
-			require.Error(t, err)
-		}
+
+			err = ss.Validate(context.Background())
+			if example.valid {
+				require.NoError(t, err)
+			} else {
+				require.Error(t, err)
+			}
+		})
 	}
 }
 
@@ -38,72 +34,65 @@ func testSecuritySchemaExample(t *testing.T, e securitySchemeExample) func(*test
 var securitySchemeExamples = []securitySchemeExample{
 	{
 		title: "Basic Authentication Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "basic"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Negotiate Authentication Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "negotiate"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Unknown http Authentication Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "notvalid"
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "API Key Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "apiKey",
   "name": "api_key",
   "in": "header"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "apiKey with bearerFormat",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "apiKey",
-	"in": "header",
-	"name": "X-API-KEY",
+  "in": "header",
+  "name": "X-API-KEY",
   "bearerFormat": "Arbitrary text"
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "Bearer Sample with arbitrary format",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "bearer",
   "bearerFormat": "Arbitrary text"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Implicit OAuth2 Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "implicit": {
@@ -114,14 +103,13 @@ var securitySchemeExamples = []securitySchemeExample{
       }
     }
   }
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "OAuth Flow Object Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "implicit": {
@@ -140,14 +128,13 @@ var securitySchemeExamples = []securitySchemeExample{
       }
     }
   }
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "OAuth Flow Object clientCredentials/password",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "clientCredentials": {
@@ -163,79 +150,71 @@ var securitySchemeExamples = []securitySchemeExample{
       }
     }
   }
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Invalid Basic",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "https",
   "scheme": "basic"
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "Apikey Cookie",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "apiKey",
 	"in": "cookie",
 	"name": "somecookie"
-}
-`),
+}`),
 		valid: true,
 	},
 
 	{
 		title: "OAuth Flow Object with no scopes",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "password": {
       "tokenUrl": "https://example.com/api/oauth/token"
     }
   }
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "OAuth Flow Object with empty scopes",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "password": {
-			"tokenUrl": "https://example.com/api/oauth/token",
-			"scopes": {}
+      "tokenUrl": "https://example.com/api/oauth/token",
+      "scopes": {}
     }
   }
-}
-`),
-		valid: true,
+}`),
+		valid: false,
 	},
+
 	{
 		title: "OIDC Type With URL",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "openIdConnect",
   "openIdConnectUrl": "https://example.com/.well-known/openid-configuration"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "OIDC Type Without URL",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "openIdConnect",
   "openIdConnectUrl": ""
-}
-`),
+}`),
 		valid: false,
 	},
 }