Skip to content

detect spec schema version #167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
28 changes: 21 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,21 @@ By default, OpenAPI v3.1 syntax is expected. To validate an OpenAPI v3.1 spec:
# If no exception is raised by validate_spec(), the spec is valid.
validate_spec(spec_dict)

validate_spec({})
validate_spec({'openapi': '3.1.0'})

Traceback (most recent call last):
...
OpenAPIValidationError: 'openapi' is a required property
OpenAPIValidationError: 'info' is a required property

In order to validate a Swagger / OpenAPI 2.0 spec file, import ``validate_v2_spec`` instead of ``validate_spec``.
In order to validate a OpenAPI 3.0 spec file, import ``validate_v30_spec`` instead of ``validate_spec``.
In order to explicitly validate a:

You can also explicitly import ``validate_v31_spec`` if you want to disambiguate the expected version or
explicitly import ``validate_v3_spec`` which is a shortcut to the latest v3 release.
* Swagger / OpenAPI 2.0 spec file, import ``validate_v2_spec``
* OpenAPI 3.0 spec file, import ``validate_v30_spec``
* OpenAPI 3.1 spec file, import ``validate_v31_spec``

instead of ``validate_spec``.

You can also explicitly import ``validate_v3_spec`` which is a shortcut to the latest v3 release.

Add ``spec_url`` to validate spec with relative files:

Expand All @@ -111,6 +115,16 @@ You can also validate spec from url:
# If no exception is raised by validate_spec_url(), the spec is valid.
validate_spec_url('http://example.com/openapi.json')

In order to explicitly validate a:

* Swagger / OpenAPI 2.0 spec file, import ``validate_v2_spec_url``
* OpenAPI 3.0 spec file, import ``validate_v30_spec_url``
* OpenAPI 3.1 spec file, import ``validate_v31_spec_url``

instead of ``validate_spec_url``.

You can also explicitly import ``validate_v3_spec_url`` which is a shortcut to the latest v3 release.

If you want to iterate through validation errors:

.. code:: python
Expand All @@ -130,4 +144,4 @@ Related projects
License
#######

Copyright (c) 2017-2021, Artur Maciag, All rights reserved. Apache v2
Copyright (c) 2017-2022, Artur Maciag, All rights reserved. Apache v2
35 changes: 22 additions & 13 deletions openapi_spec_validator/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from jsonschema_spec.handlers import default_handlers

from openapi_spec_validator.shortcuts import (
validate_spec_factory, validate_spec_url_factory,
)
from openapi_spec_validator.shortcuts import validate_spec_detect_factory
from openapi_spec_validator.shortcuts import validate_spec_url_detect_factory
from openapi_spec_validator.shortcuts import validate_spec_factory
from openapi_spec_validator.shortcuts import validate_spec_url_factory
from openapi_spec_validator.validation import openapi_v2_spec_validator
from openapi_spec_validator.validation import openapi_v3_spec_validator
from openapi_spec_validator.validation import openapi_v30_spec_validator
Expand Down Expand Up @@ -33,22 +34,30 @@
]

# shortcuts
validate_v2_spec = validate_spec_factory(openapi_v2_spec_validator.validate)
validate_spec = validate_spec_detect_factory({
("swagger", "2.0"): openapi_v2_spec_validator,
("openapi", "3.0"): openapi_v30_spec_validator,
("openapi", "3.1"): openapi_v31_spec_validator,
},
)
validate_spec_url = validate_spec_url_detect_factory({
("swagger", "2.0"): openapi_v2_spec_validator,
("openapi", "3.0"): openapi_v30_spec_validator,
("openapi", "3.1"): openapi_v31_spec_validator,
},
)
validate_v2_spec = validate_spec_factory(openapi_v2_spec_validator)
validate_v2_spec_url = validate_spec_url_factory(
openapi_v2_spec_validator.validate, default_handlers)
openapi_v2_spec_validator)

validate_v30_spec = validate_spec_factory(openapi_v30_spec_validator.validate)
validate_v30_spec = validate_spec_factory(openapi_v30_spec_validator)
validate_v30_spec_url = validate_spec_url_factory(
openapi_v30_spec_validator.validate, default_handlers)
openapi_v30_spec_validator)

validate_v31_spec = validate_spec_factory(openapi_v31_spec_validator.validate)
validate_v31_spec = validate_spec_factory(openapi_v31_spec_validator)
validate_v31_spec_url = validate_spec_url_factory(
openapi_v31_spec_validator.validate, default_handlers)
openapi_v31_spec_validator)

# aliases to the latest v3 version
validate_v3_spec = validate_v31_spec
validate_v3_spec_url = validate_v31_spec_url

# aliases to the latest version
validate_spec = validate_v3_spec
validate_spec_url = validate_v3_spec_url
6 changes: 6 additions & 0 deletions openapi_spec_validator/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class OpenAPISpecValidatorError(Exception):
pass


class ValidatorDetectError(OpenAPISpecValidatorError):
pass
40 changes: 32 additions & 8 deletions openapi_spec_validator/shortcuts.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
"""OpenAPI spec validator shortcuts module."""
import urllib.parse

from jsonschema_spec.handlers import all_urls_handler

def validate_spec_factory(validator_callable):
from openapi_spec_validator.exceptions import ValidatorDetectError


def detect_validator(choices, spec):
for (key, value), validator in choices.items():
if key in spec and spec[key].startswith(value):
return validator
raise ValidatorDetectError("Spec schema version not detected")


def validate_spec_detect_factory(choices):
def validate(spec, spec_url=''):
return validator_callable(spec, spec_url=spec_url)
validator_class = detect_validator(choices, spec)
return validator_class.validate(spec, spec_url=spec_url)
return validate


def validate_spec_factory(validator_class):
def validate(spec, spec_url=''):
return validator_class.validate(spec, spec_url=spec_url)
return validate


def validate_spec_url_detect_factory(choices):
def validate(spec_url):
spec = all_urls_handler(spec_url)
validator_class = detect_validator(choices, spec)
return validator_class.validate(spec, spec_url=spec_url)
return validate


def validate_spec_url_factory(validator_callable, handlers):
def validate(url):
result = urllib.parse.urlparse(url)
handler = handlers[result.scheme]
spec = handler(url)
return validator_callable(spec, spec_url=url)
def validate_spec_url_factory(validator_class):
def validate(spec_url):
spec = all_urls_handler(spec_url)
return validator_class.validate(spec, spec_url=spec_url)
return validate
1 change: 1 addition & 0 deletions tests/integration/data/empty.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
42 changes: 33 additions & 9 deletions tests/integration/test_shortcuts.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
import pytest
from jsonschema_spec.handlers import default_handlers

from openapi_spec_validator import (
validate_spec, validate_spec_url,
validate_v2_spec, validate_v2_spec_url,
validate_spec_factory, validate_spec_url_factory,
openapi_v2_spec_validator, openapi_v30_spec_validator,
validate_v30_spec_url, validate_v30_spec,
)
from openapi_spec_validator.exceptions import ValidatorDetectError
from openapi_spec_validator.validation.exceptions import OpenAPIValidationError


class TestLocalOpenAPIv20Validator:
class TestValidateSpec:

def test_spec_schema_version_not_detected(self):
spec = {}

with pytest.raises(ValidatorDetectError):
validate_spec(spec)


class TestValidateSpecUrl:

def test_spec_schema_version_not_detected(self, factory):
spec_path = "data/empty.yaml"
spec_url = factory.spec_file_url(spec_path)

with pytest.raises(ValidatorDetectError):
validate_spec_url(spec_url)


class TestValidatev2Spec:

LOCAL_SOURCE_DIRECTORY = "data/v2.0/"

Expand All @@ -25,10 +45,11 @@ def test_valid(self, factory, spec_file):
spec = factory.spec_from_file(spec_path)
spec_url = factory.spec_file_url(spec_path)

validate_spec(spec)
validate_v2_spec(spec)

validate_spec_factory(
openapi_v2_spec_validator.validate)(spec, spec_url)
openapi_v2_spec_validator)(spec, spec_url)

@pytest.mark.parametrize('spec_file', [
"empty.yaml",
Expand All @@ -41,7 +62,7 @@ def test_falied(self, factory, spec_file):
validate_v2_spec(spec)


class TestLocalOpenAPIv30Validator:
class TestValidatev30Spec:

LOCAL_SOURCE_DIRECTORY = "data/v3.0/"

Expand All @@ -56,10 +77,11 @@ def test_valid(self, factory, spec_file):
spec = factory.spec_from_file(spec_path)
spec_url = factory.spec_file_url(spec_path)

validate_spec(spec)
validate_v30_spec(spec)

validate_spec_factory(
openapi_v30_spec_validator.validate)(spec, spec_url)
openapi_v30_spec_validator)(spec, spec_url)

@pytest.mark.parametrize('spec_file', [
"empty.yaml",
Expand All @@ -72,7 +94,7 @@ def test_falied(self, factory, spec_file):
validate_v30_spec(spec)


class TestRemoteValidateV20:
class TestValidatev2SpecUrl:

REMOTE_SOURCE_URL = (
"https://raw.githubusercontent.com/OAI/OpenAPI-Specification/"
Expand All @@ -92,13 +114,14 @@ def remote_test_suite_file_path(self, test_file):
def test_valid(self, spec_file):
spec_url = self.remote_test_suite_file_path(spec_file)

validate_spec_url(spec_url)
validate_v2_spec_url(spec_url)

validate_spec_url_factory(
openapi_v2_spec_validator.validate, default_handlers)(spec_url)
openapi_v2_spec_validator)(spec_url)


class TestRemoteValidateV30:
class TestValidatev30SpecUrl:

REMOTE_SOURCE_URL = (
"https://raw.githubusercontent.com/OAI/OpenAPI-Specification/"
Expand All @@ -118,7 +141,8 @@ def remote_test_suite_file_path(self, test_file):
def test_valid(self, spec_file):
spec_url = self.remote_test_suite_file_path(spec_file)

validate_spec_url(spec_url)
validate_v30_spec_url(spec_url)

validate_spec_url_factory(
openapi_v30_spec_validator.validate, default_handlers)(spec_url)
openapi_v30_spec_validator)(spec_url)