pax_global_header00006660000000000000000000000064143303331210014503gustar00rootroot0000000000000052 comment=10edb95ebbdf0782eb7a6a5ba9c06ede5ad5b338 swagger_spec_validator-3.0.3/000077500000000000000000000000001433033312100162245ustar00rootroot00000000000000swagger_spec_validator-3.0.3/.appveyor.yml000066400000000000000000000007441433033312100206770ustar00rootroot00000000000000version: '{build}' image: Visual Studio 2019 environment: matrix: # Available python versions and their locations on https://www.appveyor.com/docs/build-environment/#python - PYTHON: C:\Python37-x64 TOXENV: py37-default - PYTHON: C:\Python38-x64 TOXENV: py38-default build: off install: - cmd: SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH% - cmd: pip install tox before_test: - cmd: python --version - cmd: pip --version - cmd: tox --version test_script: - cmd: tox swagger_spec_validator-3.0.3/.github/000077500000000000000000000000001433033312100175645ustar00rootroot00000000000000swagger_spec_validator-3.0.3/.github/workflows/000077500000000000000000000000001433033312100216215ustar00rootroot00000000000000swagger_spec_validator-3.0.3/.github/workflows/ci.yml000066400000000000000000000013131433033312100227350ustar00rootroot00000000000000--- name: build on: push jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: - '3.7' - '3.8' steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - run: pip install tox - run: tox -e py misc: runs-on: ubuntu-18.04 strategy: fail-fast: false matrix: tox: - pre-commit - cover steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: python-version: 3.7 - run: pip install tox - run: tox -e ${{ matrix.tox }} swagger_spec_validator-3.0.3/.github/workflows/publish.yml000066400000000000000000000011271433033312100240130ustar00rootroot00000000000000name: Publish on PyPI on: push: tags: - v* jobs: publish: runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v2 with: python-version: 3.7 - name: Install Python dependencies run: pip install wheel - name: Create a Wheel file and source distribution run: python setup.py sdist bdist_wheel - name: Publish distribution package to PyPI uses: pypa/gh-action-pypi-publish@v1.2.2 with: password: ${{ secrets.pypi_password }} swagger_spec_validator-3.0.3/.gitignore000066400000000000000000000002421433033312100202120ustar00rootroot00000000000000*.pyc *.swp .cache/ .coverage .idea/ .pytest_cache/ .python-version .tox .vscode/ build/ dist/ docs/build MANIFEST swagger_spec_validator.egg-info virtualenv_run swagger_spec_validator-3.0.3/.pre-commit-config.yaml000066400000000000000000000015671433033312100225160ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 hooks: - id: check-json - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: fix-encoding-pragma args: [--remove] - id: name-tests-test - id: trailing-whitespace - id: requirements-txt-fixer files: requirements-dev.txt - id: pretty-format-json args: - --autofix - --indent=4 - repo: https://github.com/asottile/reorder_python_imports rev: v3.8.5 hooks: - id: reorder-python-imports exclude: ^(docs/.*|setup.py)$ - repo: https://github.com/asottile/pyupgrade rev: v3.1.0 hooks: - id: pyupgrade - repo: http://github.com/psf/black rev: 22.10.0 hooks: - id: black language_version: python3.7 args: [--target-version, py37] swagger_spec_validator-3.0.3/CHANGELOG.rst000066400000000000000000000114701433033312100202500ustar00rootroot00000000000000Changelog ========= 3.0.2 (2022-10-20) ------------------ - Fix location of py.typed file 3.0.1 (2022-10-19) ------------------ - Add py.typed marker - PR #158 - Enforce python_requires>=3.7 3.0.0 (2022-10-19) ------------------ - Add type annotations - PR #157 - Drop py2 and py36 support - PR #157 - Drop simplejson - PR #157 2.7.4 (2021-10-08) ------------------ - Make checks for unique operation ids global instead of per tag - PR #145 2.7.3 (2020-06-25) ------------------ - Fix recursive ref resolution for some specs split across multiple files - PR #140 2.7.2 (2020-06-10) ------------------ - Fix recursive definition detection - PR #139 2.7.1 (2020-06-09) ------------------ - Fix: `additionalProperties` can be a boolean value - PR #138 2.7.0 (2020-06-05) ------------------ - Ensure correct validation into additionalProperties and items specifications - PR #134. Thanks brycedrennan for your contribution. 2.6.0 (2020-05-20) ------------------ - Improve validation performance in case of consecutive validations. - PR #132. Thanks brycedrennan for your contribution. 2.5.0 (2020-02-25) ------------------ - Use ``yaml.CSafeLoader`` if available - PR #122 - Show definition name when raising ``SwaggerValidationError`` - PR #124 2.4.3 (2019-01-16) ------------------ - Fix regression, introduced by PR #111, that was causing descending references with no scope. - PR #113 2.4.2 (2019-01-15) ------------------ - Add warning when ``$ref`` is defined to have ``None`` value - PR #111 - Ensure that only valid references (``$ref`` attribute is present with string value) are dereferenced - PR #111 2.4.1 (2018-10-09) ------------------ - Add warning when using ``$ref`` together with other properties - PR #107 2.4.0 (2018-08-28) ------------------ - Disallow multiple types in schema definitions. See `OpenAPI#458 `_ for context - PR #106 2.3.1 (2018-06-11) ------------------ - Fix urlopen issue on Windows platform. Issue #99, PR #101 2.3.0 (2018-06-06) ------------------ - Ensure that inline models are validated - #97 - Ensure that parameters are validated - #97 - Validation of defaults set to None is skipped if x-nullable is set - #97 2.2.0 (2018-06-05) ------------------ - Add support for reading YAML files - #74 - Make sure operationIds are unique within the same tag - #93 - Validate that array models in the top-level definitions have an ``items`` property (validation for array models in other places will come in a future release) - #95 - Responses (the mapping of HTTP status codes to Response objects) cannot be a reference - #92 - ``$ref`` values need to be strings - #83. Thanks ceridwen for your contribution! - Ensure that default values are compliant with the spec - #82 - More helpful error message when encountering unresolvable path params - #72. Thanks daym for your contribution! 2.1.0 (2017-03-21) ------------------ - Properly validate polymorphic objects - #68 2.0.4 (2017-03-10) ------------------ - Rename package to swagger-spec-validator to fix PyPI upload issues - #59 2.0.3 (2016-11-23) ------------------ - Ignore x- vendor extensions in the schema at the #/paths/{path}/{http_method} level - PR #45 - Swagger 2.0 schema synced with upstream - PR #40 2.0.2 (2015-11-18) ------------------ - Fix regression with Swagger 1.2 schemas - #43 2.0.1 (2015-11-17) ------------------ - Fix rich validations that rely on a working deref with scope annotations 2.0.0 (2015-11-17) ------------------ - Support for recursive $refs - Unqualified $refs no longer supported. Bad: ``{"$ref": "User"}`` Good: ``{"$ref": "#/definitions/User"}`` 1.1.1 (2015-10-02) ------------------ - Workaround for validation of Swagger 2.0 schemas with external refs - #38 1.1.0 (2015-08-24) ------------------ - Validate crossrefs - #33, #34 1.0.12 (2015-07-02) ------------------- - Handle API level parameters - #29 - More robust handling of $refs - #29 1.0.11 (2015-06-02) ------------------- - Validation for model name and it (Swagger 1.2) - Remove unnecessary dependency on pyyaml 1.0.10 (2015-04-15) ------------------- - Pin of jsonschema used for unit tests for Python3 1.0.9 (2015-03-26) ------------------ - Sync Swagger 2.0 schema with upstream - allow empty arrays for parameter - Handle schemas with no definitions 1.0.8 (2015-03-11) ------------------ - Petstore URLs updated - Support 'type: File' for (Swagger 1.2) 1.0.7 (2015-03-02) ------------------ - Python3 support - Use simplejson when available 1.0.5 (2015-02-19) ------------------ - Add file:// support 1.0.3 (2015-01-05) ------------------ - Initial support for Swagger 2.0 1.0.2 (2014-10-24) ------------------ - Bugfix for path construction in validate_resource_listing_url 1.0.1 (2014-10-24) ------------------ - Bugfix to including jsonschema files 1.0.0 (2014-10-24) ------------------ - Initial version swagger_spec_validator-3.0.3/LICENSE.txt000066400000000000000000000010421433033312100200440ustar00rootroot00000000000000Copyright 2015 Yelp Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. swagger_spec_validator-3.0.3/MANIFEST.in000066400000000000000000000003601433033312100177610ustar00rootroot00000000000000include README.md include LICENSE.txt # Required by python 2.6 even though we include these in our setup.py include swagger_spec_validator/schemas/v1.2/* include swagger_spec_validator/schemas/v2.0/* include swagger_spec_validator/py.typed swagger_spec_validator-3.0.3/Makefile000066400000000000000000000004101433033312100176570ustar00rootroot00000000000000.PHONY: docs test clean test: tox docs: tox -e docs clean: find . -name '*.pyc' -delete rm -rf swagger_validator.egg-info rm -rf docs/build rm -f MANIFEST rm -rf .tox .PHONY: install-hooks install-hooks: tox -e pre-commit -- install -f --install-hooks swagger_spec_validator-3.0.3/README.md000066400000000000000000000032141433033312100175030ustar00rootroot00000000000000# swagger_spec_validator [![Build Status](https://github.com/Yelp/swagger_spec_validator/workflows/build/badge.svg?branch=master)](https://github.com/Yelp/swagger_spec_validator/actions?query=workflow%3Abuild) [![Coverage Status](https://coveralls.io/repos/Yelp/swagger_spec_validator/badge.svg)](https://coveralls.io/r/Yelp/swagger_spec_validator) [![Latest Version](https://img.shields.io/pypi/v/swagger_spec_validator.svg)](https://pypi.python.org/pypi/swagger_spec_validator/) ## About Swagger Spec Validator is a Python library that validates Swagger Specs against the [Swagger 1.2](https://github.com/swagger-api/swagger-spec/blob/master/versions/1.2.md) or [Swagger 2.0](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md) specification. The validator aims to check for full compliance with the Specification. ## Example Usage Validate a spec from a url: ```python from swagger_spec_validator import validate_spec_url # example for swagger spec v1.2 validate_spec_url('http://petstore.swagger.io/api/api-docs') # example for swagger spec v2.0 validate_spec_url('http://petstore.swagger.io/v2/swagger.json') ``` ## Documentation More documentation is available at http://swagger_spec_validator.readthedocs.org ## Installation $ pip install swagger_spec_validator ## Contributing 1. Fork it ( http://github.com/Yelp/swagger_spec_validator/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request ## License Copyright (c) 2015, Yelp, Inc. All rights reserved. Apache v2 swagger_spec_validator-3.0.3/docs/000077500000000000000000000000001433033312100171545ustar00rootroot00000000000000swagger_spec_validator-3.0.3/docs/source/000077500000000000000000000000001433033312100204545ustar00rootroot00000000000000swagger_spec_validator-3.0.3/docs/source/changelog.rst000077700000000000000000000000001433033312100255772../../CHANGELOG.rstustar00rootroot00000000000000swagger_spec_validator-3.0.3/docs/source/conf.py000066400000000000000000000125341433033312100217600ustar00rootroot00000000000000# # swagger_spec_validator documentation build configuration file, created by # sphinx-quickstart on Fri May 13 14:16:02 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os import sphinx_rtd_theme # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("../..")) sys.path.insert(0, os.path.abspath("../extensions")) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinx.ext.coverage", ] try: import sphinx.ext.viewcode extensions.append("sphinx.ext.viewcode") except ImportError: pass try: # if it's available, use sphinx_http_domain import sphinx_http_domain extensions.append("sphinx_http_domain") except ImportError: pass # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "Swagger Spec Validator" copyright = "Yelp Inc" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # base_dir = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) about = {} with open(os.path.join(base_dir, "swagger_spec_validator", "__about__.py")) as f: exec(f.read(), about) version = release = about["__version__"] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] html_static_path = ["_static"] intersphinx_mapping = {"http://docs.python.org/": None} html_use_index = True htmlhelp_basename = "swagger_spec_validatordoc" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). # latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). # latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ( "index", "swagger_spec_validator.tex", "swagger_spec_validator Documentation", "Yelp Inc", "manual", ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Additional stuff for the LaTeX preamble. # latex_preamble = '' # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ( "index", "swagger_spec_validator", "Swagger Spec Validator Documentation", ["Yelp Inc"], 1, ) ] # Example configuration for intersphinx: refer to the Python standard library. # intersphinx_mapping = {'http://docs.python.org/': None} swagger_spec_validator-3.0.3/docs/source/index.rst000066400000000000000000000021041433033312100223120ustar00rootroot00000000000000.. swagger spec validator documentation master file, created by sphinx-quickstart on Fri May 13 14:16:02 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Swagger Spec Validator ********************** Introduction ============ Swagger Spec Validator is a Python library that validates *Swagger Specs* against the `Swagger 1.2`_ or `Swagger 2.0`_ specification. The validator aims to check for full compliance with the Specification. Frequently Asked Questions ========================== Is the API stable? ------------------ Probably not. However, it is currently very simple, so it shouldn't be hard to upgrade code if there's a non backwards-compatible change. API === .. currentmodule:: swagger_spec_validator .. autoclass:: SwaggerValidationError .. autofunction:: validate_spec_url .. toctree:: :maxdepth: 1 changelog .. _Swagger 1.2: https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md .. _Swagger 2.0: https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md swagger_spec_validator-3.0.3/mypy.ini000066400000000000000000000005171433033312100177260ustar00rootroot00000000000000[mypy] python_version = 3.7 show_error_context = true show_column_numbers = true warn_unused_ignores = true warn_unreachable = true disallow_untyped_defs = true disallow_incomplete_defs = true disallow_untyped_calls = true disallow_untyped_decorators = true disallow_subclassing_any = true [mypy-cffi.*] ignore_missing_imports = true swagger_spec_validator-3.0.3/requirements-dev.txt000066400000000000000000000001261433033312100222630ustar00rootroot00000000000000httpretty mock mypy pytest>=3.1.0 types-jsonschema types-pyyaml types-setuptools zipp swagger_spec_validator-3.0.3/setup.cfg000066400000000000000000000000311433033312100200370ustar00rootroot00000000000000[wheel] universal = True swagger_spec_validator-3.0.3/setup.py000066400000000000000000000023521433033312100177400ustar00rootroot00000000000000import os from setuptools import find_packages, setup base_dir = os.path.dirname(__file__) about = {} with open(os.path.join(base_dir, "swagger_spec_validator", "__about__.py")) as f: exec(f.read(), about) install_requires = [ "jsonschema", "pyyaml", "typing-extensions", ] setup( name=about["__title__"], version=about["__version__"], description=about["__summary__"], url=about["__uri__"], author=about["__author__"], author_email=about["__email__"], packages=find_packages(exclude=["tests", "tests.*"]), package_data={ "swagger_spec_validator": [ "swagger_spec_validator/schemas/v1.2/*", "swagger_spec_validator/schemas/v2.0/*", "swagger_spec_validator/py.typed", ], }, python_requires=">=3.7", include_package_data=True, install_requires=install_requires, license=about["__license__"], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Operating System :: OS Independent", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", ], ) swagger_spec_validator-3.0.3/swagger_spec_validator/000077500000000000000000000000001433033312100227425ustar00rootroot00000000000000swagger_spec_validator-3.0.3/swagger_spec_validator/__about__.py000066400000000000000000000006351433033312100252260ustar00rootroot00000000000000__all__ = [ "__title__", "__summary__", "__uri__", "__version__", "__author__", "__email__", "__license__", ] __title__ = "swagger-spec-validator" __summary__ = "Validation of Swagger specifications" __uri__ = "http://github.com/Yelp/swagger_spec_validator" __version__ = "3.0.3" __author__ = "Yelp" __email__ = "core-services@yelp.com" __license__ = "Apache License, Version 2.0" swagger_spec_validator-3.0.3/swagger_spec_validator/__init__.py000066400000000000000000000003011433033312100250450ustar00rootroot00000000000000from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.util import validate_spec_url __all__ = [ "SwaggerValidationError", "validate_spec_url", ] swagger_spec_validator-3.0.3/swagger_spec_validator/common.py000066400000000000000000000041211433033312100246020ustar00rootroot00000000000000from __future__ import annotations import contextlib import functools import os import sys from functools import lru_cache from typing import Any from typing import Callable from typing import TypeVar from urllib.parse import urljoin from urllib.request import pathname2url from urllib.request import urlopen import yaml from pkg_resources import resource_filename from typing_extensions import ParamSpec try: from yaml import CSafeLoader as SafeLoader except ImportError: # pragma: no cover from yaml import SafeLoader # type: ignore TIMEOUT_SEC = 1.0 P = ParamSpec("P") T = TypeVar("T") def wrap_exception(method: Callable[P, T]) -> Callable[P, T]: @functools.wraps(method) def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: try: return method(*args, **kwargs) except Exception as e: raise SwaggerValidationError(str(e), e).with_traceback(sys.exc_info()[2]) return wrapper def get_uri_from_file_path(file_path: str) -> str: return urljoin("file://", pathname2url(os.path.abspath(file_path))) def read_file(file_path: str) -> dict[str, Any]: """ Utility method for reading a JSON/YAML file and converting it to a Python dictionary :param file_path: path of the file to read :return: Python dictionary representation of the JSON file :rtype: dict """ return read_url(get_uri_from_file_path(file_path)) @lru_cache() def read_resource_file(resource_path: str) -> tuple[dict[str, Any], str]: schema_path = resource_filename("swagger_spec_validator", resource_path) return read_file(schema_path), schema_path def read_url(url: str, timeout: float = TIMEOUT_SEC) -> dict[str, Any]: with contextlib.closing(urlopen(url, timeout=timeout)) as fh: # NOTE: JSON is a subset of YAML so it is safe to read JSON as it is YAML return yaml.load(fh.read().decode("utf-8"), Loader=SafeLoader) class SwaggerValidationError(Exception): """Exception raised in case of a validation error.""" pass class SwaggerValidationWarning(UserWarning): """Warning raised during validation.""" pass swagger_spec_validator-3.0.3/swagger_spec_validator/py.typed000066400000000000000000000000001433033312100244270ustar00rootroot00000000000000swagger_spec_validator-3.0.3/swagger_spec_validator/ref_validators.py000066400000000000000000000205701433033312100263240ustar00rootroot00000000000000from __future__ import annotations import contextlib import functools import logging from collections.abc import Mapping from typing import Any from typing import Callable from typing import Generator from typing import TYPE_CHECKING from jsonschema import validators from jsonschema.validators import Draft4Validator from jsonschema.validators import RefResolver if TYPE_CHECKING: from jsonschema.exceptions import _Error from jsonschema.validators import _Validator from swagger_spec_validator import common log = logging.getLogger(__name__) default_handlers = { "http": common.read_url, "https": common.read_url, "file": common.read_url, } def validate( instance: object, schema: Mapping[str, Any], instance_cls: type[_Validator], cls: type[_Validator] | None = None, *args: Any, **kwargs: Any, ) -> None: """This is a carbon-copy of :method:`jsonschema.validate` except that it takes two validator classes instead of just one. In the jsonschema implementation, `cls` is used to validate both the schema and the instance. This changes the behavior to have a separate validator for each of schema and instance. Schema should not be validated with the custom validator returned by :method:`create_dereffing_validator` because it follows $refs. :param instance: the instance to validate :param schema: the schema to validate with :param instance_cls: Validator class to validate instance. :param cls: Validator class to validate schema. :raises: :exc:`ValidationError` if the instance is invalid :exc:`SchemaError` if the schema itself is invalid """ if cls is None: cls = validators.validator_for(schema) cls.check_schema(schema) instance_cls(schema, *args, **kwargs).validate(instance) def create_dereffing_validator(instance_resolver: RefResolver) -> type[_Validator]: """Create a customized Draft4Validator that follows $refs in the schema being validated (the Swagger spec for a service). This is not to be confused with $refs that are in the schema that describes the Swagger 2.0 specification. :param instance_resolver: resolver for the swagger service's spec :type instance_resolver: :class:`jsonschema.RefResolver` :rtype: Its complicated. See jsonschema.validators.create() """ visited_refs: dict[str, str] = {} validators_to_bound = { "$ref", "additionalProperties", "allOf", "anyOf", "dependencies", "maxProperties", "minProperties", "not", "oneOf", "patternProperties", "properties", "required", "type", } bound_validators = { k: functools.partial( validator_wrapper, instance_resolver=instance_resolver, visited_refs=visited_refs, default_validator_callable=v, ) if k in validators_to_bound else v for k, v in Draft4Validator.VALIDATORS.items() } return validators.extend(Draft4Validator, bound_validators) def validate_schema_value( schema: Mapping[str, Any], value: Any, swagger_resolver: RefResolver | None = None, ) -> None: # pass resolver to avoid to refetch schema files if swagger_resolver is None: swagger_resolver = RefResolver.from_schema(schema) create_dereffing_validator(swagger_resolver)( schema, resolver=swagger_resolver ).validate(value) @contextlib.contextmanager def visiting(visited_refs: dict[str, str], ref: str) -> Generator[None, None, None]: """Context manager that keeps track of $refs that we've seen during validation. :param visited_refs: dict of $refs :param ref: string $ref value """ visited_refs[ref] = ref try: yield finally: del visited_refs[ref] def validator_wrapper( validator: type[_Validator], schema_element: Any, instance: dict[str, Any], schema: Mapping[str, Any], instance_resolver: RefResolver, visited_refs: dict[str, str], default_validator_callable: Callable, ) -> Generator[_Error, None, None]: """Generator function that parameterizes default_validator_callable. :type validator: :class:`jsonschema.validators.Validator` :param schema_element: The schema element that is passed in to each specific validator callable aka the 2nd arg in each jsonschema._validators.* callable. :param instance: The fragment of the swagger service spec that is being validated. :param schema: The fragment of the swagger jsonschema spec that describes is used for validation. :param instance_resolver: Resolves refs in the swagger service spec :param visited_refs: Keeps track of visisted refs during validation of the swagger service spec. :param default_validator_callable: jsonschema._validators.* callable """ yield from deref_and_validate( validator, schema_element, instance, schema, instance_resolver, visited_refs, default_validator_callable, ) def deref_and_validate( validator: type[_Validator], schema_element: Any, instance: dict[str, Any], schema: Mapping[str, Any], instance_resolver: RefResolver, visited_refs: dict[str, str], default_validator_callable: Callable, ) -> Generator[_Error, None, None]: """Generator function that dereferences instance if it is a $ref before passing it downstream for actual validation. When a cyclic ref is detected, short-circuit and return. :type validator: :class:`jsonschema.validators.Validator` :param schema_element: The schema element that is passed in to each specific validator callable aka the 2nd arg in each jsonschema._validators.* callable. :param instance: The fragment of the swagger service spec that is being validated. :param schema: The fragment of the swagger jsonschema spec that describes is used for validation. :param instance_resolver: Resolves refs in the swagger service spec :param visited_refs: Keeps track of visisted refs during validation of the swagger service spec. :param default_validator_callable: jsonschema._validators.* callable """ if ( isinstance(instance, dict) and "$ref" in instance and isinstance(instance["$ref"], str) ): ref = instance["$ref"] # Annotate $ref dict with scope - used by custom validations # We still need to attach the scope even if this is a cycle, as otherwise there are cases # with specs split into multiple files where it can't be dereferenced properly attach_scope(instance, instance_resolver) if ref in visited_refs: log.debug("Found cycle in %s", ref) return with visiting(visited_refs, ref): with instance_resolver.resolving(ref) as target: yield from default_validator_callable( validator, schema_element, target, schema ) else: yield from default_validator_callable( validator, schema_element, instance, schema ) def attach_scope(ref_dict: dict[str, Any], instance_resolver: RefResolver) -> None: """Attach scope to each $ref we encounter so that the $ref can be resolved by custom validations done outside the scope of jsonscema validations. :param ref_dict: dict with $ref key :type instance_resolver: :class:`jsonschema.RefResolver` """ if "x-scope" in ref_dict: log.debug("Ref %s already has scope attached", ref_dict["$ref"]) return log.debug("Attaching x-scope to %s", ref_dict) ref_dict["x-scope"] = list(instance_resolver._scopes_stack) # type: ignore @contextlib.contextmanager def in_scope( resolver: RefResolver, ref_dict: dict[str, Any] ) -> Generator[None, None, None]: """Context manager to assume the given scope for the passed in resolver. The resolver's original scope is restored when exiting the context manager. :type resolver: :class:`jsonschema.RefResolver :type ref_dict: dict """ if "x-scope" not in ref_dict: yield else: saved_scope_stack = resolver._scopes_stack # type: ignore try: resolver._scopes_stack = ref_dict["x-scope"] # type: ignore yield finally: resolver._scopes_stack = saved_scope_stack # type: ignore swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/000077500000000000000000000000001433033312100243655ustar00rootroot00000000000000swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/000077500000000000000000000000001433033312100250535ustar00rootroot00000000000000swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/apiDeclaration.json000066400000000000000000000040551433033312100306710ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "additionalProperties": false, "definitions": { "apiObject": { "additionalProperties": false, "properties": { "description": { "type": "string" }, "operations": { "items": { "$ref": "operationObject.json#" }, "type": "array" }, "path": { "format": "uri-template", "pattern": "^/", "type": "string" } }, "required": [ "path", "operations" ], "type": "object" }, "mimeTypeArray": { "items": { "format": "mime-type", "type": "string" }, "type": "array" } }, "properties": { "apiVersion": { "type": "string" }, "apis": { "items": { "$ref": "#/definitions/apiObject" }, "type": "array" }, "authorizations": { "$ref": "authorizationObject.json#" }, "basePath": { "format": "uri", "pattern": "^http://", "type": "string" }, "consumes": { "$ref": "#/definitions/mimeTypeArray" }, "models": { "additionalProperties": { "$ref": "modelsObject.json#" }, "type": "object" }, "produces": { "$ref": "#/definitions/mimeTypeArray" }, "resourcePath": { "format": "uri", "pattern": "^/", "type": "string" }, "swaggerVersion": { "enum": [ "1.2" ] } }, "required": [ "swaggerVersion", "basePath", "apis" ], "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/authorizationObject.json000066400000000000000000000045251433033312100320030ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/basicAuth" }, { "$ref": "#/definitions/apiKey" }, { "$ref": "#/definitions/oauth2" } ] }, "definitions": { "apiKey": { "additionalProperties": false, "properties": { "keyname": { "type": "string" }, "passAs": { "enum": [ "header", "query" ] }, "type": { "enum": [ "apiKey" ] } }, "required": [ "type", "passAs", "keyname" ] }, "basicAuth": { "additionalProperties": false, "properties": { "type": { "enum": [ "basicAuth" ] } }, "required": [ "type" ] }, "oauth2": { "additionalProperties": false, "properties": { "grantTypes": { "$ref": "oauth2GrantType.json#" }, "scopes": { "items": { "$ref": "#/definitions/oauth2Scope" }, "type": "array" }, "type": { "enum": [ "oauth2" ] } }, "required": [ "type", "grantTypes" ], "type": "object" }, "oauth2Scope": { "additionalProperties": false, "properties": { "description": { "type": "string" }, "scope": { "type": "string" } }, "required": [ "scope" ], "type": "object" } }, "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/dataType.json000066400000000000000000000153311433033312100275240ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "arrayType": { "additionalProperties": false, "properties": { "items": { "items": { "$ref": "#/definitions/itemsObject" }, "type": "array" }, "type": { "enum": [ "array" ] }, "uniqueItems": { "type": "boolean" } }, "required": [ "type", "items" ] }, "itemsObject": { "oneOf": [ { "$ref": "#/definitions/refType" }, { "allOf": [ { "$ref": "#/definitions/primitiveType" }, { "additionalProperties": false, "properties": { "format": {}, "type": {} } } ] } ] }, "modelType": { "additionalProperties": false, "properties": { "type": { "not": { "enum": [ "boolean", "integer", "number", "string", "array" ] }, "type": "string" } }, "required": [ "type" ] }, "primitiveType": { "additionalProperties": false, "dependencies": { "enum": { "properties": { "type": { "enum": [ "string" ] } } }, "format": { "oneOf": [ { "properties": { "format": { "enum": [ "int32", "int64" ] }, "type": { "enum": [ "integer" ] } } }, { "properties": { "format": { "enum": [ "float", "double" ] }, "type": { "enum": [ "number" ] } } }, { "properties": { "format": { "enum": [ "byte", "date", "date-time" ] }, "type": { "enum": [ "string" ] } } } ] }, "maximum": { "properties": { "type": { "enum": [ "integer", "number" ] } } }, "minimum": { "properties": { "type": { "enum": [ "integer", "number" ] } } } }, "properties": { "defaultValue": { "not": { "type": [ "array", "object", "null" ] } }, "enum": { "items": { "type": "string" }, "minItems": 1, "type": "array", "uniqueItems": true }, "format": { "type": "string" }, "maximum": { "type": "string" }, "minimum": { "type": "string" }, "type": { "enum": [ "boolean", "integer", "number", "string" ] } }, "required": [ "type" ] }, "refType": { "additionalProperties": false, "properties": { "$ref": { "type": "string" } }, "required": [ "$ref" ] }, "voidType": { "enum": [ { "type": "void" } ] } }, "description": "Data type as described by the specification (version 1.2)", "oneOf": [ { "$ref": "#/definitions/refType" }, { "$ref": "#/definitions/voidType" }, { "$ref": "#/definitions/primitiveType" }, { "$ref": "#/definitions/modelType" }, { "$ref": "#/definitions/arrayType" } ], "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/dataTypeBase.json000066400000000000000000000073261433033312100303240ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "itemsObject": { "oneOf": [ { "additionalProperties": false, "properties": { "$ref": { "type": "string" } }, "required": [ "$ref" ], "type": "object" }, { "allOf": [ { "$ref": "#" }, { "additionalProperties": false, "properties": { "format": {}, "type": {} }, "required": [ "type" ] } ] } ] } }, "dependencies": { "format": { "oneOf": [ { "properties": { "format": { "enum": [ "int32", "int64" ] }, "type": { "enum": [ "integer" ] } } }, { "properties": { "format": { "enum": [ "float", "double" ] }, "type": { "enum": [ "number" ] } } }, { "properties": { "format": { "enum": [ "byte", "date", "date-time" ] }, "type": { "enum": [ "string" ] } } } ] } }, "description": "Data type fields (section 4.3.3)", "oneOf": [ { "required": [ "type" ] }, { "required": [ "$ref" ] } ], "properties": { "$ref": { "type": "string" }, "defaultValue": { "not": { "type": [ "array", "object", "null" ] } }, "enum": { "items": { "type": "string" }, "minItems": 1, "type": "array", "uniqueItems": true }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/itemsObject" }, "maximum": { "type": "string" }, "minimum": { "type": "string" }, "type": { "type": "string" }, "uniqueItems": { "type": "boolean" } }, "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/infoObject.json000066400000000000000000000013301433033312100300250ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "additionalProperties": false, "description": "info object (section 5.1.3)", "properties": { "contact": { "format": "email", "type": "string" }, "description": { "type": "string" }, "license": { "type": "string" }, "licenseUrl": { "format": "uri", "type": "string" }, "termsOfServiceUrl": { "format": "uri", "type": "string" }, "title": { "type": "string" } }, "required": [ "title", "description" ], "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/modelsObject.json000066400000000000000000000021071433033312100303600ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "propertyObject": { "allOf": [ { "not": { "$ref": "#" } }, { "$ref": "dataTypeBase.json#" } ] } }, "dependencies": { "subTypes": [ "discriminator" ] }, "properties": { "description": { "type": "string" }, "discriminator": { "type": "string" }, "id": { "type": "string" }, "properties": { "additionalProperties": { "$ref": "#/definitions/propertyObject" }, "type": "object" }, "subTypes": { "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "required": [ "id", "properties" ], "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/oauth2GrantType.json000066400000000000000000000047521433033312100310160ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "authorizationCode": { "additionalProperties": false, "properties": { "tokenEndpoint": { "$ref": "#/definitions/tokenEndpoint" }, "tokenRequestEndpoint": { "$ref": "#/definitions/tokenRequestEndpoint" } }, "required": [ "tokenEndpoint", "tokenRequestEndpoint" ], "type": "object" }, "implicit": { "additionalProperties": false, "properties": { "loginEndpoint": { "$ref": "#/definitions/loginEndpoint" }, "tokenName": { "type": "string" } }, "required": [ "loginEndpoint" ], "type": "object" }, "loginEndpoint": { "additionalProperties": false, "properties": { "url": { "format": "uri", "type": "string" } }, "required": [ "url" ], "type": "object" }, "tokenEndpoint": { "additionalProperties": false, "properties": { "tokenName": { "type": "string" }, "url": { "format": "uri", "type": "string" } }, "required": [ "url" ], "type": "object" }, "tokenRequestEndpoint": { "additionalProperties": false, "properties": { "clientIdName": { "type": "string" }, "clientSecretName": { "type": "string" }, "url": { "format": "uri", "type": "string" } }, "required": [ "url" ], "type": "object" } }, "minProperties": 1, "properties": { "authorization_code": { "$ref": "#/definitions/authorizationCode" }, "implicit": { "$ref": "#/definitions/implicit" } }, "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/operationObject.json000066400000000000000000000057201433033312100311010ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "allOf": [ { "$ref": "dataTypeBase.json#" }, { "properties": { "authorizations": { "additionalProperties": { "items": { "$ref": "authorizationObject.json#/definitions/oauth2Scope" }, "type": "array" }, "type": "object" }, "consumes": { "$ref": "#/definitions/mimeTypeArray" }, "deprecated": { "enum": [ "true", "false" ] }, "method": { "enum": [ "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD" ] }, "nickname": { "pattern": "^[a-zA-Z0-9_]+$", "type": "string" }, "notes": { "type": "string" }, "parameters": { "items": { "$ref": "parameterObject.json#" }, "type": "array" }, "produces": { "$ref": "#/definitions/mimeTypeArray" }, "responseMessages": { "items": { "$ref": "#/definitions/responseMessageObject" }, "type": "array" }, "summary": { "maxLength": 120, "type": "string" } }, "required": [ "method", "nickname", "parameters" ] } ], "definitions": { "mimeTypeArray": { "items": { "format": "mime-type", "type": "string" }, "type": "array" }, "responseMessageObject": { "properties": { "code": { "$ref": "#/definitions/rfc2616section10" }, "message": { "type": "string" }, "responseModel": { "type": "string" } }, "required": [ "code", "message" ], "type": "object" }, "rfc2616section10": { "exclusiveMaximum": true, "maximum": 600, "minimum": 100, "type": "integer" } }, "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/parameterObject.json000066400000000000000000000037631433033312100310660ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "allOf": [ { "$ref": "dataTypeBase.json#" }, { "properties": { "allowMultiple": { "type": "boolean" }, "description": { "type": "string" }, "name": { "type": "string" }, "paramType": { "enum": [ "path", "query", "body", "header", "form" ] }, "required": { "type": "boolean" } }, "required": [ "paramType", "name" ] }, { "description": "type File requires special paramType and consumes", "oneOf": [ { "properties": { "type": { "not": { "enum": [ "File" ] } } } }, { "properties": { "consumes": { "enum": [ "multipart/form-data" ] }, "paramType": { "enum": [ "form" ] }, "type": { "enum": [ "File" ] } } } ] } ], "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/resourceListing.json000066400000000000000000000011711433033312100311270ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "properties": { "apiVersion": { "type": "string" }, "apis": { "items": { "$ref": "resourceObject.json#" }, "type": "array" }, "authorizations": { "$ref": "authorizationObject.json#" }, "info": { "$ref": "infoObject.json#" }, "swaggerVersion": { "enum": [ "1.2" ] } }, "required": [ "swaggerVersion", "apis" ], "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v1.2/resourceObject.json000066400000000000000000000005201433033312100307210ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "additionalProperties": false, "properties": { "description": { "type": "string" }, "path": { "format": "uri", "type": "string" } }, "required": [ "path" ], "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v2.0/000077500000000000000000000000001433033312100250525ustar00rootroot00000000000000swagger_spec_validator-3.0.3/swagger_spec_validator/schemas/v2.0/schema.json000066400000000000000000001465121433033312100272160ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "additionalProperties": false, "definitions": { "apiKeySecurity": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string" }, "in": { "enum": [ "header", "query" ], "type": "string" }, "name": { "type": "string" }, "type": { "enum": [ "apiKey" ], "type": "string" } }, "required": [ "type", "name", "in" ], "type": "object" }, "basicAuthenticationSecurity": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string" }, "type": { "enum": [ "basic" ], "type": "string" } }, "required": [ "type" ], "type": "object" }, "bodyParameter": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed.", "type": "string" }, "in": { "description": "Determines the location of the parameter.", "enum": [ "body" ], "type": "string" }, "name": { "description": "The name of the parameter.", "type": "string" }, "required": { "default": false, "description": "Determines whether or not this parameter is required or optional.", "type": "boolean" }, "schema": { "$ref": "#/definitions/schema" } }, "required": [ "name", "in", "schema" ], "type": "object" }, "collectionFormat": { "default": "csv", "enum": [ "csv", "ssv", "tsv", "pipes" ], "type": "string" }, "collectionFormatWithMulti": { "default": "csv", "enum": [ "csv", "ssv", "tsv", "pipes", "multi" ], "type": "string" }, "contact": { "additionalProperties": false, "description": "Contact information for the owners of the API.", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "email": { "description": "The email address of the contact person/organization.", "format": "email", "type": "string" }, "name": { "description": "The identifying name of the contact person/organization.", "type": "string" }, "url": { "description": "The URL pointing to the contact information.", "format": "uri", "type": "string" } }, "type": "object" }, "default": { "$ref": "http://json-schema.org/draft-04/schema#/properties/default" }, "definitions": { "additionalProperties": { "$ref": "#/definitions/schema" }, "description": "One or more JSON objects describing the schemas being consumed and produced by the API.", "type": "object" }, "description": { "$ref": "http://json-schema.org/draft-04/schema#/properties/description" }, "enum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" }, "examples": { "additionalProperties": true, "type": "object" }, "exclusiveMaximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" }, "externalDocs": { "additionalProperties": false, "description": "information about external documentation", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string" }, "url": { "format": "uri", "type": "string" } }, "required": [ "url" ], "type": "object" }, "fileSchema": { "additionalProperties": false, "description": "A deterministic version of a JSON Schema object.", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "default": { "$ref": "http://json-schema.org/draft-04/schema#/properties/default" }, "description": { "$ref": "http://json-schema.org/draft-04/schema#/properties/description" }, "example": {}, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "format": { "type": "string" }, "readOnly": { "default": false, "type": "boolean" }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "type": { "enum": [ "file" ], "type": "string" } }, "required": [ "type" ], "type": "object" }, "formDataParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "allowEmptyValue": { "default": false, "description": "allows sending a parameter by name only or with an empty value.", "type": "boolean" }, "collectionFormat": { "$ref": "#/definitions/collectionFormatWithMulti" }, "default": { "$ref": "#/definitions/default" }, "description": { "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed.", "type": "string" }, "enum": { "$ref": "#/definitions/enum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "format": { "type": "string" }, "in": { "description": "Determines the location of the parameter.", "enum": [ "formData" ], "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "maximum": { "$ref": "#/definitions/maximum" }, "minItems": { "$ref": "#/definitions/minItems" }, "minLength": { "$ref": "#/definitions/minLength" }, "minimum": { "$ref": "#/definitions/minimum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" }, "name": { "description": "The name of the parameter.", "type": "string" }, "pattern": { "$ref": "#/definitions/pattern" }, "required": { "default": false, "description": "Determines whether or not this parameter is required or optional.", "type": "boolean" }, "type": { "enum": [ "string", "number", "boolean", "integer", "array", "file" ], "type": "string" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" } } }, "header": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "description": { "type": "string" }, "enum": { "$ref": "#/definitions/enum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "maximum": { "$ref": "#/definitions/maximum" }, "minItems": { "$ref": "#/definitions/minItems" }, "minLength": { "$ref": "#/definitions/minLength" }, "minimum": { "$ref": "#/definitions/minimum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" }, "pattern": { "$ref": "#/definitions/pattern" }, "type": { "enum": [ "string", "number", "integer", "boolean", "array" ], "type": "string" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" } }, "required": [ "type" ], "type": "object" }, "headerParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "description": { "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed.", "type": "string" }, "enum": { "$ref": "#/definitions/enum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "format": { "type": "string" }, "in": { "description": "Determines the location of the parameter.", "enum": [ "header" ], "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "maximum": { "$ref": "#/definitions/maximum" }, "minItems": { "$ref": "#/definitions/minItems" }, "minLength": { "$ref": "#/definitions/minLength" }, "minimum": { "$ref": "#/definitions/minimum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" }, "name": { "description": "The name of the parameter.", "type": "string" }, "pattern": { "$ref": "#/definitions/pattern" }, "required": { "default": false, "description": "Determines whether or not this parameter is required or optional.", "type": "boolean" }, "type": { "enum": [ "string", "number", "boolean", "integer", "array" ], "type": "string" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" } } }, "headers": { "additionalProperties": { "$ref": "#/definitions/header" }, "type": "object" }, "info": { "additionalProperties": false, "description": "General information about the API.", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "contact": { "$ref": "#/definitions/contact" }, "description": { "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed.", "type": "string" }, "license": { "$ref": "#/definitions/license" }, "termsOfService": { "description": "The terms of service for the API.", "type": "string" }, "title": { "description": "A unique and precise title of the API.", "type": "string" }, "version": { "description": "A semantic version number of the API.", "type": "string" } }, "required": [ "version", "title" ], "type": "object" }, "jsonReference": { "additionalProperties": false, "properties": { "$ref": { "type": "string" } }, "required": [ "$ref" ], "type": "object" }, "license": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "name": { "description": "The name of the license type. It's encouraged to use an OSI compatible license.", "type": "string" }, "url": { "description": "The URL pointing to the license.", "format": "uri", "type": "string" } }, "required": [ "name" ], "type": "object" }, "maxItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "maxLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "maximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" }, "mediaTypeList": { "items": { "$ref": "#/definitions/mimeType" }, "type": "array", "uniqueItems": true }, "mimeType": { "description": "The MIME type of the HTTP message.", "type": "string" }, "minItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "minLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "minimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" }, "multipleOf": { "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" }, "nonBodyParameter": { "oneOf": [ { "$ref": "#/definitions/headerParameterSubSchema" }, { "$ref": "#/definitions/formDataParameterSubSchema" }, { "$ref": "#/definitions/queryParameterSubSchema" }, { "$ref": "#/definitions/pathParameterSubSchema" } ], "required": [ "name", "in", "type" ], "type": "object" }, "oauth2AccessCodeSecurity": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "authorizationUrl": { "format": "uri", "type": "string" }, "description": { "type": "string" }, "flow": { "enum": [ "accessCode" ], "type": "string" }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "tokenUrl": { "format": "uri", "type": "string" }, "type": { "enum": [ "oauth2" ], "type": "string" } }, "required": [ "type", "flow", "authorizationUrl", "tokenUrl" ], "type": "object" }, "oauth2ApplicationSecurity": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string" }, "flow": { "enum": [ "application" ], "type": "string" }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "tokenUrl": { "format": "uri", "type": "string" }, "type": { "enum": [ "oauth2" ], "type": "string" } }, "required": [ "type", "flow", "tokenUrl" ], "type": "object" }, "oauth2ImplicitSecurity": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "authorizationUrl": { "format": "uri", "type": "string" }, "description": { "type": "string" }, "flow": { "enum": [ "implicit" ], "type": "string" }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "type": { "enum": [ "oauth2" ], "type": "string" } }, "required": [ "type", "flow", "authorizationUrl" ], "type": "object" }, "oauth2PasswordSecurity": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string" }, "flow": { "enum": [ "password" ], "type": "string" }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "tokenUrl": { "format": "uri", "type": "string" }, "type": { "enum": [ "oauth2" ], "type": "string" } }, "required": [ "type", "flow", "tokenUrl" ], "type": "object" }, "oauth2Scopes": { "additionalProperties": { "type": "string" }, "type": "object" }, "operation": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "consumes": { "$ref": "#/definitions/mediaTypeList", "description": "A list of MIME types the API can consume." }, "deprecated": { "default": false, "type": "boolean" }, "description": { "description": "A longer description of the operation, GitHub Flavored Markdown is allowed.", "type": "string" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "operationId": { "description": "A unique identifier of the operation.", "type": "string" }, "parameters": { "$ref": "#/definitions/parametersList" }, "produces": { "$ref": "#/definitions/mediaTypeList", "description": "A list of MIME types the API can produce." }, "responses": { "$ref": "#/definitions/responses" }, "schemes": { "$ref": "#/definitions/schemesList" }, "security": { "$ref": "#/definitions/security" }, "summary": { "description": "A brief summary of the operation.", "type": "string" }, "tags": { "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "required": [ "responses" ], "type": "object" }, "parameter": { "oneOf": [ { "$ref": "#/definitions/bodyParameter" }, { "$ref": "#/definitions/nonBodyParameter" } ] }, "parameterDefinitions": { "additionalProperties": { "$ref": "#/definitions/parameter" }, "description": "One or more JSON representations for parameters", "type": "object" }, "parametersList": { "additionalItems": false, "description": "The parameters needed to send a valid API call.", "items": { "oneOf": [ { "$ref": "#/definitions/parameter" }, { "$ref": "#/definitions/jsonReference" } ] }, "type": "array", "uniqueItems": true }, "pathItem": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "$ref": { "type": "string" }, "delete": { "$ref": "#/definitions/operation" }, "get": { "$ref": "#/definitions/operation" }, "head": { "$ref": "#/definitions/operation" }, "options": { "$ref": "#/definitions/operation" }, "parameters": { "$ref": "#/definitions/parametersList" }, "patch": { "$ref": "#/definitions/operation" }, "post": { "$ref": "#/definitions/operation" }, "put": { "$ref": "#/definitions/operation" } }, "type": "object" }, "pathParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "description": { "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed.", "type": "string" }, "enum": { "$ref": "#/definitions/enum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "format": { "type": "string" }, "in": { "description": "Determines the location of the parameter.", "enum": [ "path" ], "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "maximum": { "$ref": "#/definitions/maximum" }, "minItems": { "$ref": "#/definitions/minItems" }, "minLength": { "$ref": "#/definitions/minLength" }, "minimum": { "$ref": "#/definitions/minimum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" }, "name": { "description": "The name of the parameter.", "type": "string" }, "pattern": { "$ref": "#/definitions/pattern" }, "required": { "description": "Determines whether or not this parameter is required or optional.", "enum": [ true ], "type": "boolean" }, "type": { "enum": [ "string", "number", "boolean", "integer", "array" ], "type": "string" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" } }, "required": [ "required" ] }, "paths": { "additionalProperties": false, "description": "Relative paths to the individual endpoints. They must be relative to the 'basePath'.", "patternProperties": { "^/": { "$ref": "#/definitions/pathItem" }, "^x-": { "$ref": "#/definitions/vendorExtension" } }, "type": "object" }, "pattern": { "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" }, "primitivesItems": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "enum": { "$ref": "#/definitions/enum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "maximum": { "$ref": "#/definitions/maximum" }, "minItems": { "$ref": "#/definitions/minItems" }, "minLength": { "$ref": "#/definitions/minLength" }, "minimum": { "$ref": "#/definitions/minimum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" }, "pattern": { "$ref": "#/definitions/pattern" }, "type": { "enum": [ "string", "number", "integer", "boolean", "array" ], "type": "string" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" } }, "type": "object" }, "queryParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "allowEmptyValue": { "default": false, "description": "allows sending a parameter by name only or with an empty value.", "type": "boolean" }, "collectionFormat": { "$ref": "#/definitions/collectionFormatWithMulti" }, "default": { "$ref": "#/definitions/default" }, "description": { "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed.", "type": "string" }, "enum": { "$ref": "#/definitions/enum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "format": { "type": "string" }, "in": { "description": "Determines the location of the parameter.", "enum": [ "query" ], "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "maximum": { "$ref": "#/definitions/maximum" }, "minItems": { "$ref": "#/definitions/minItems" }, "minLength": { "$ref": "#/definitions/minLength" }, "minimum": { "$ref": "#/definitions/minimum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" }, "name": { "description": "The name of the parameter.", "type": "string" }, "pattern": { "$ref": "#/definitions/pattern" }, "required": { "default": false, "description": "Determines whether or not this parameter is required or optional.", "type": "boolean" }, "type": { "enum": [ "string", "number", "boolean", "integer", "array" ], "type": "string" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" } } }, "response": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string" }, "examples": { "$ref": "#/definitions/examples" }, "headers": { "$ref": "#/definitions/headers" }, "schema": { "oneOf": [ { "$ref": "#/definitions/schema" }, { "$ref": "#/definitions/fileSchema" } ] } }, "required": [ "description" ], "type": "object" }, "responseDefinitions": { "additionalProperties": { "$ref": "#/definitions/response" }, "description": "One or more JSON representations for parameters", "type": "object" }, "responseValue": { "oneOf": [ { "$ref": "#/definitions/response" }, { "$ref": "#/definitions/jsonReference" } ] }, "responses": { "additionalProperties": false, "description": "Response objects names can either be any valid HTTP status code or 'default'.", "minProperties": 1, "not": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "type": "object" }, "patternProperties": { "^([0-9]{3})$|^(default)$": { "$ref": "#/definitions/responseValue" }, "^x-": { "$ref": "#/definitions/vendorExtension" } }, "type": "object" }, "schema": { "additionalProperties": false, "description": "A deterministic version of a JSON Schema object.", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "$ref": { "type": "string" }, "additionalProperties": { "anyOf": [ { "$ref": "#/definitions/schema" }, { "type": "boolean" } ], "default": {} }, "allOf": { "items": { "$ref": "#/definitions/schema" }, "minItems": 1, "type": "array" }, "default": { "$ref": "http://json-schema.org/draft-04/schema#/properties/default" }, "description": { "$ref": "http://json-schema.org/draft-04/schema#/properties/description" }, "discriminator": { "type": "string" }, "enum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" }, "example": {}, "exclusiveMaximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" }, "exclusiveMinimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "format": { "type": "string" }, "items": { "anyOf": [ { "$ref": "#/definitions/schema" }, { "items": { "$ref": "#/definitions/schema" }, "minItems": 1, "type": "array" } ], "default": {} }, "maxItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "maxLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "maxProperties": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "maximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" }, "minItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "minLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "minProperties": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "minimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" }, "multipleOf": { "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" }, "pattern": { "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" }, "properties": { "additionalProperties": { "$ref": "#/definitions/schema" }, "default": {}, "type": "object" }, "readOnly": { "default": false, "type": "boolean" }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "type": { "$ref": "http://json-schema.org/draft-04/schema#/properties/type" }, "uniqueItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" }, "xml": { "$ref": "#/definitions/xml" } }, "type": "object" }, "schemesList": { "description": "The transfer protocol of the API.", "items": { "enum": [ "http", "https", "ws", "wss" ], "type": "string" }, "type": "array", "uniqueItems": true }, "security": { "items": { "$ref": "#/definitions/securityRequirement" }, "type": "array", "uniqueItems": true }, "securityDefinitions": { "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/basicAuthenticationSecurity" }, { "$ref": "#/definitions/apiKeySecurity" }, { "$ref": "#/definitions/oauth2ImplicitSecurity" }, { "$ref": "#/definitions/oauth2PasswordSecurity" }, { "$ref": "#/definitions/oauth2ApplicationSecurity" }, { "$ref": "#/definitions/oauth2AccessCodeSecurity" } ] }, "type": "object" }, "securityRequirement": { "additionalProperties": { "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "type": "object" }, "tag": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "name": { "type": "string" } }, "required": [ "name" ], "type": "object" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "uniqueItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" }, "vendorExtension": { "additionalItems": true, "additionalProperties": true, "description": "Any property starting with x- is valid." }, "xml": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "attribute": { "default": false, "type": "boolean" }, "name": { "type": "string" }, "namespace": { "type": "string" }, "prefix": { "type": "string" }, "wrapped": { "default": false, "type": "boolean" } }, "type": "object" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "basePath": { "description": "The base path to the API. Example: '/api'.", "pattern": "^/", "type": "string" }, "consumes": { "$ref": "#/definitions/mediaTypeList", "description": "A list of MIME types accepted by the API." }, "definitions": { "$ref": "#/definitions/definitions" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "host": { "description": "The host (name or ip) of the API. Example: 'swagger.io'", "pattern": "^[^{}/ :\\\\]+(?::\\d+)?$", "type": "string" }, "info": { "$ref": "#/definitions/info" }, "parameters": { "$ref": "#/definitions/parameterDefinitions" }, "paths": { "$ref": "#/definitions/paths" }, "produces": { "$ref": "#/definitions/mediaTypeList", "description": "A list of MIME types the API can produce." }, "responses": { "$ref": "#/definitions/responseDefinitions" }, "schemes": { "$ref": "#/definitions/schemesList" }, "security": { "$ref": "#/definitions/security" }, "securityDefinitions": { "$ref": "#/definitions/securityDefinitions" }, "swagger": { "description": "The Swagger version of this document.", "enum": [ "2.0" ], "type": "string" }, "tags": { "items": { "$ref": "#/definitions/tag" }, "type": "array", "uniqueItems": true } }, "required": [ "swagger", "info", "paths" ], "title": "A JSON Schema for Swagger 2.0 API.", "type": "object" } swagger_spec_validator-3.0.3/swagger_spec_validator/util.py000066400000000000000000000043221433033312100242720ustar00rootroot00000000000000from __future__ import annotations import logging from types import ModuleType from typing import Any from swagger_spec_validator import validator12 from swagger_spec_validator import validator20 from swagger_spec_validator.common import read_url from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.common import wrap_exception log = logging.getLogger(__name__) def get_validator(spec_json: dict[str, Any], origin: str = "unknown") -> ModuleType: """ :param spec_json: Dict representation of the json API spec :param origin: filename or url of the spec - only use for error messages :return: module responsible for validation based on Swagger version in the spec """ swagger12_version = spec_json.get("swaggerVersion") swagger20_version = spec_json.get("swagger") if swagger12_version and swagger20_version: raise SwaggerValidationError( "You've got conflicting keys for the Swagger version in your spec. " "Expected `swaggerVersion` or `swagger`, but not both." ) elif swagger12_version and swagger12_version == "1.2": # we don't care about versions prior to 1.2 return validator12 elif swagger20_version and swagger20_version == "2.0": return validator20 elif swagger12_version is None and swagger20_version is None: raise SwaggerValidationError( "Swagger spec {} missing version. Expected " "`swaggerVersion` or `swagger`".format(origin) ) else: raise SwaggerValidationError( "Swagger version {} not supported.".format( swagger12_version or swagger20_version ) ) @wrap_exception def validate_spec_url(spec_url: str) -> None: """Validates a Swagger spec given its URL. :param spec_url: For Swagger 1.2, this is the URL to the resource listing in api-docs. For Swagger 2.0, this is the URL to swagger.json in api-docs. If given as ``file://``, this must be an absolute url for cross-refs to work correctly. """ spec_json = read_url(spec_url) validator = get_validator(spec_json, spec_url) validator.validate_spec(spec_json, spec_url) swagger_spec_validator-3.0.3/swagger_spec_validator/validator12.py000066400000000000000000000230231433033312100254440ustar00rootroot00000000000000""" Validate Swagger Specs against the Swagger 1.2 Specification. The validator aims to check for full compliance with the Specification. The validator uses the published jsonschema files for basic structural validation, augmented with custom validation code where necessary. https://github.com/swagger-api/swagger-spec/blob/master/versions/1.2.md """ from __future__ import annotations import logging import os from typing import Any from urllib.parse import urlparse import jsonschema from jsonschema import RefResolver from swagger_spec_validator.common import get_uri_from_file_path from swagger_spec_validator.common import read_resource_file from swagger_spec_validator.common import read_url from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.common import wrap_exception from swagger_spec_validator.ref_validators import default_handlers log = logging.getLogger(__name__) # Primitives (§4.3.1) PRIMITIVE_TYPES = ["integer", "number", "string", "boolean"] def get_model_ids(api_declaration: dict[str, Any]) -> list[str]: models = api_declaration.get("models", {}) return [model["id"] for model in models.values()] def get_resource_path(url: str, resource: str) -> str: """Fetch the complete resource path to get the api declaration. :param url: A file or http uri hosting the resource listing. :type url: string :param resource: Resource path starting with a '/'. eg. '/pet' :type resource: string :returns: Complete resource path hosting the api declaration. """ if urlparse(url).scheme == "file": parent_dir = os.path.dirname(url) def resource_file_name(resource: str) -> str: assert resource.startswith("/") return resource[1:] + ".json" path = os.path.join(parent_dir, resource_file_name(resource)) else: path = url + resource return path @wrap_exception def validate_spec_url(url: str) -> None: """Simple utility function to perform recursive validation of a Resource Listing and all associated API Declarations. This is trivial wrapper function around :py:func:`swagger_spec_validator.validate_resource_listing` and :py:func:`swagger_spec_validator.validate_api_declaration`. You are encouraged to write your own version of this if required. :param url: the URL of the Resource Listing. :returns: `None` in case of success, otherwise raises an exception. :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ log.info("Validating %s", url) validate_spec(read_url(url), url) def validate_spec(resource_listing: dict[str, Any], url: str) -> None: """ Validates the resource listing, fetches the api declarations and consequently validates them as well. :type resource_listing: dict :param url: url serving the resource listing; needed to resolve api declaration path. :type url: string :returns: `None` in case of success, otherwise raises an exception. :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ validate_resource_listing(resource_listing) for api in resource_listing["apis"]: path = get_resource_path(url, api["path"]) log.info("Validating %s", path) validate_api_declaration(read_url(path)) def validate_data_type( obj: dict[str, Any], model_ids: list[str], allow_arrays: bool = True, allow_voids: bool = False, allow_refs: bool = True, allow_file: bool = False, ) -> None: """Validate an object that contains a data type (§4.3.3). Params: - obj: the dictionary containing the data type to validate - model_ids: a list of model ids - allow_arrays: whether an array is permitted in the data type. This is used to prevent nested arrays. - allow_voids: whether a void type is permitted. This is used when validating Operation Objects (§5.2.3). - allow_refs: whether '$ref's are permitted. If true, then 'type's are not allowed to reference model IDs. """ typ = obj.get("type") ref = obj.get("$ref") # TODO Use a custom jsonschema.Validator to Validate defaultValue # enum, minimum, maximum, uniqueItems if typ is not None: if typ in PRIMITIVE_TYPES: return if allow_voids and typ == "void": return if typ == "array": if not allow_arrays: raise SwaggerValidationError('"array" not allowed') # Items Object (§4.3.4) items = obj.get("items") if items is None: raise SwaggerValidationError('"items" not found') validate_data_type(items, model_ids, allow_arrays=False) return if typ == "File": if not allow_file: raise SwaggerValidationError( 'Type "File" is only valid for form parameters' ) return if typ in model_ids: if allow_refs: raise SwaggerValidationError( 'must use "$ref" for referencing "%s"' % typ ) return raise SwaggerValidationError('unknown type "%s"' % typ) if ref is not None: if not allow_refs: raise SwaggerValidationError('"$ref" not allowed') if ref not in model_ids: raise SwaggerValidationError('unknown model id "%s"' % ref) return raise SwaggerValidationError('no "$ref" or "type" present') def validate_model( model: dict[str, Any], model_name: str, model_ids: list[str] ) -> None: """Validate a Model Object (§5.2.7).""" # TODO Validate 'sub-types' and 'discriminator' fields for required in model.get("required", []): if required not in model["properties"]: raise SwaggerValidationError( 'Model "{}": required property "{}" not found'.format( model_name, required ) ) if model_name != model["id"]: error = "model name: {} does not match model id: {}".format( model_name, model["id"] ) raise SwaggerValidationError(error) for prop_name, prop in model.get("properties", {}).items(): try: validate_data_type(prop, model_ids, allow_refs=True) except SwaggerValidationError as e: # Add more context to the exception and re-raise raise SwaggerValidationError( 'Model "{}", property "{}": {}'.format(model_name, prop_name, str(e)) ) def validate_parameter(parameter: dict[str, Any], model_ids: list[str]) -> None: """Validate a Parameter Object (§5.2.4).""" allow_file = parameter.get("paramType") == "form" validate_data_type(parameter, model_ids, allow_refs=False, allow_file=allow_file) def validate_operation(operation: dict[str, Any], model_ids: list[str]) -> None: """Validate an Operation Object (§5.2.3).""" try: validate_data_type(operation, model_ids, allow_refs=False, allow_voids=True) except SwaggerValidationError as e: raise SwaggerValidationError( 'Operation "{}": {}'.format(operation["nickname"], str(e)) ) for parameter in operation["parameters"]: try: validate_parameter(parameter, model_ids) except SwaggerValidationError as e: raise SwaggerValidationError( 'Operation "%s", parameter "%s": %s' % (operation["nickname"], parameter["name"], str(e)) ) def validate_api(api: dict[str, Any], model_ids: list[str]) -> None: """Validate an API Object (§5.2.2).""" for operation in api["operations"]: validate_operation(operation, model_ids) def validate_api_declaration(api_declaration: dict[str, Any]) -> None: """Validate an API Declaration (§5.2). :param api_declaration: a dictionary respresentation of an API Declaration. :returns: `None` in case of success, otherwise raises an exception. :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` :raises: :py:class:`jsonschema.exceptions.ValidationError` """ validate_json(api_declaration, "schemas/v1.2/apiDeclaration.json") model_ids = get_model_ids(api_declaration) for api in api_declaration["apis"]: validate_api(api, model_ids) for model_name, model in api_declaration.get("models", {}).items(): validate_model(model, model_name, model_ids) def validate_resource_listing(resource_listing: dict[str, Any]) -> None: """Validate a Resource Listing (§5.1). :param resource_listing: a dictionary respresentation of a Resource Listing. Note that you will have to invoke `validate_api_declaration` on each linked API Declaration. :returns: `None` in case of success, otherwise raises an exception. :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` :raises: :py:class:`jsonschema.exceptions.ValidationError` """ validate_json(resource_listing, "schemas/v1.2/resourceListing.json") @wrap_exception def validate_json(json_document: list[Any] | dict[str, Any], schema_path: str) -> None: """Validate a json document against a json schema. :param json_document: json document in the form of a list or dict. :param schema_path: package relative path of the json schema file. """ schema, schema_path = read_resource_file(schema_path) resolver = RefResolver( base_uri=get_uri_from_file_path(schema_path), referrer=schema, handlers=default_handlers, ) jsonschema.validate(json_document, schema, resolver=resolver) swagger_spec_validator-3.0.3/swagger_spec_validator/validator20.py000066400000000000000000000635011433033312100254500ustar00rootroot00000000000000from __future__ import annotations import functools import json import logging import string import warnings from typing import Any from typing import Callable from typing import cast from typing import Iterable from typing import TYPE_CHECKING if TYPE_CHECKING: from _typeshed import SupportsKeysAndGetItem from jsonschema.validators import _Handler from jsonschema.validators import Draft4Validator from jsonschema.validators import RefResolver from swagger_spec_validator import ref_validators from swagger_spec_validator.common import get_uri_from_file_path from swagger_spec_validator.common import read_resource_file from swagger_spec_validator.common import read_url from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.common import SwaggerValidationWarning from swagger_spec_validator.common import wrap_exception from swagger_spec_validator.ref_validators import default_handlers from swagger_spec_validator.ref_validators import in_scope from swagger_spec_validator.ref_validators import validate_schema_value log = logging.getLogger(__name__) def validate_ref(ref_dict: dict[str, Any], path: list[str]) -> None: """Check if a ref_dict has siblings that will be overwritten by $ref or $ref is None. While the siblings case does not contradict the spec, it may cause confusion and mislead developers. See https://stackoverflow.com/a/48114924. :param ref_dict: A dict that may be {'$ref': '#/blah/blah', 'x-nullable': true}. :type ref_dict: dict :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ keys_to_ignore = {"x-scope", "$ref", "description"} if any(key for key in ref_dict.keys() if key not in keys_to_ignore): warnings.warn( SwaggerValidationWarning( 'Found "$ref: {}" with siblings that will be overwritten. ' "See https://stackoverflow.com/a/48114924 for more information. (path {})".format( ref_dict["$ref"], "/".join(path), ) ), ) if ref_dict["$ref"] is None: warnings.warn( SwaggerValidationWarning( "Identified $ref with None value. This is usually an error, " "although technically it might be allowed. (path: {})".format( "/".join(path) ), ), ) def validate_references( raw_spec: dict[Any, Any] | list[Any], deref: Callable, path: list[str] | None = None, visited_spec_ids: set[int] | None = None, ) -> None: if path is None: path = ["#"] if visited_spec_ids is None: visited_spec_ids = set() if id(raw_spec) in visited_spec_ids: return # Register raw_spec into visited_spec_ids to prevent unbounded recursion visited_spec_ids.add(id(raw_spec)) if isinstance(raw_spec, dict) and "$ref" in raw_spec: # The additional check is needed as `is_ref` will consider raw_spec dictionaries with `$ref` # attribute and string value. # The goal of validate_ref is to ensure that objects that looks like a reference is actually # valid by warning users about those cases. # Due to _looks like_ we need to almost duplicate the check by removing the string enforcement validate_ref(ref_dict=raw_spec, path=path) if is_ref(raw_spec) and "x-scope" in raw_spec: # Ensure that we're following references only if they were already # descended by jsonschema during initial schema validation (x-scope is present) # This is mostly done to ensure that we're not causing RefResolutionError # due to the fact that the reference is relative and we have no information # about the base path. # This could be handled differently but we thought that checking references that # were actively part of the specs is a valid trade-off here validate_references(deref(raw_spec), deref, path, visited_spec_ids) elif isinstance(raw_spec, dict): for k, v in sorted(raw_spec.items()): validate_references(v, deref, path + [k], visited_spec_ids) elif isinstance(raw_spec, list): for k, v in enumerate(raw_spec): validate_references(v, deref, path + [str(k)], visited_spec_ids) def deref( ref_dict: dict[Any, Any], resolver: RefResolver ) -> int | float | None | bool | list[Any] | dict[Any, Any]: """Dereference ref_dict (if it is indeed a ref) and return what the ref points to. :param ref_dict: Something like {'$ref': '#/blah/blah'} :type ref_dict: dict :param resolver: Ref resolver used to do the de-referencing :type resolver: :class:`jsonschema.RefResolver` :return: de-referenced value of ref_dict :rtype: scalar, list, dict """ if ref_dict is None or not is_ref(ref_dict): return ref_dict ref = ref_dict["$ref"] with in_scope(resolver, ref_dict): with resolver.resolving(ref) as target: log.debug("Resolving %s", ref) return target @wrap_exception def validate_spec_url(spec_url: str) -> RefResolver: """Validates a Swagger 2.0 API Specification at the given URL. :param spec_url: the URL of the service's swagger spec. :returns: The resolver (with cached remote refs) used during validation :rtype: :class:`jsonschema.RefResolver` :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ log.info("Validating %s", spec_url) return validate_spec(read_url(spec_url), spec_url) def validate_spec( spec_dict: dict[Any, Any], spec_url: str = "", http_handlers: SupportsKeysAndGetItem[str, _Handler] | Iterable[tuple[str, _Handler]] | None = None, ) -> RefResolver: """Validates a Swagger 2.0 API Specification given a Swagger Spec. :param spec_dict: the json dict of the swagger spec. :type spec_dict: dict :param spec_url: url from which spec_dict was retrieved. Used for dereferencing refs. eg: file:///foo/swagger.json :type spec_url: string :param http_handlers: used to download any remote $refs in spec_dict with a custom http client. Defaults to None in which case the default http client built into jsonschema's RefResolver is used. This is a mapping from uri scheme to a callable that takes a uri. :returns: the resolver (with cached remote refs) used during validation :rtype: :class:`jsonschema.RefResolver` :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ swagger_resolver = validate_json( spec_dict, "schemas/v2.0/schema.json", spec_url=spec_url, http_handlers=http_handlers, ) bound_deref = functools.partial(deref, resolver=swagger_resolver) spec_dict = cast("dict[Any, Any]", bound_deref(spec_dict)) apis = bound_deref(spec_dict["paths"]) definitions = bound_deref(spec_dict.get("definitions", {})) validate_apis(cast("dict[Any, Any]", apis), bound_deref) validate_definitions(cast("dict[Any, Any]", definitions), bound_deref) validate_parameters( cast("dict[Any, Any]", bound_deref(spec_dict.get("parameters", {}))), bound_deref, ) validate_references(spec_dict, bound_deref) return swagger_resolver @wrap_exception def validate_json( spec_dict: list[Any] | dict[str, Any], schema_path: str, spec_url: str = "", http_handlers: SupportsKeysAndGetItem[str, _Handler] | Iterable[tuple[str, _Handler]] | None = None, ) -> RefResolver: """Validate a json document against a json schema. :param spec_dict: json document in the form of a list or dict. :param schema_path: package relative path of the json schema file. :param spec_url: base uri to use when creating a RefResolver for the passed in spec_dict. :param http_handlers: used to download any remote $refs in spec_dict with a custom http client. Defaults to None in which case the default http client built into jsonschema's RefResolver is used. This is a mapping from uri scheme to a callable that takes a uri. :return: RefResolver for spec_dict with cached remote $refs used during validation. :rtype: :class:`jsonschema.RefResolver` """ schema, schema_path = read_resource_file(schema_path) schema_resolver = RefResolver( base_uri=get_uri_from_file_path(schema_path), referrer=schema, handlers=default_handlers, ) spec_resolver = RefResolver( base_uri=spec_url, referrer=cast("dict[str, Any]", spec_dict), handlers=http_handlers or default_handlers, ) ref_validators.validate( instance=spec_dict, schema=schema, resolver=schema_resolver, instance_cls=ref_validators.create_dereffing_validator(spec_resolver), cls=Draft4Validator, ) # Since remote $refs were downloaded, pass the resolver back to the caller # so that its cached $refs can be re-used. return spec_resolver @wrap_exception def validate_value_type(schema: dict[str, Any], value: Any, deref: Callable) -> None: # Extract resolver from deref partial build on ``validate_spec`` # This is used in order to use already fetched external references # If it is missing a new RefResolver will be initialized swagger_resolver = getattr(deref, "keywords", {}).get("resolver", None) validate_schema_value( schema=deref(schema), value=value, swagger_resolver=swagger_resolver ) def validate_default_in_parameter(param_spec: dict[str, Any], deref: Callable) -> None: deref_param_spec = deref(param_spec) if deref_param_spec.get("required"): # If the parameter is a required parameter, default has no meaning return if "default" in deref_param_spec: if ( deref_param_spec["default"] is None and deref_param_spec.get("x-nullable", False) is True ): # In case x-nullable property is set to true, null is a valid default return validate_value_type( schema=deref_param_spec, value=deref_param_spec["default"], deref=deref, ) def validate_defaults_in_parameters(params_spec: list[Any], deref: Callable) -> None: """ Validates that default values for api parameters are of the parameter type :param params_spec: list of parameter objects (#/paths///parameters) :param deref: callable that dereferences $refs :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ # Note: this functions is preserved to avoid public signature updates (it's not used internally) for param_spec in params_spec: validate_default_in_parameter(param_spec, deref) def validate_responses( api: str, http_verb: str, responses_dict: dict[str, Any], deref: Callable ) -> None: if is_ref(responses_dict): raise SwaggerValidationError( "{http_verb} {api} does not have a valid responses section. " "That section cannot be just a reference to another object.".format( http_verb=http_verb.upper(), api=api, ) ) for response_status, response_spec in responses_dict.items(): response_schema = response_spec.get("schema") if response_schema is None: continue validate_definition( definition=response_schema, deref=deref, def_name="#/paths/{api}/{http_verb}/responses/{status_code}".format( http_verb=http_verb, api=api, status_code=response_status, ), visited_definitions=set(), ) def validate_non_body_parameter( param: dict[str, Any], deref: Callable, def_name: str ) -> None: if "type" not in param: raise SwaggerValidationError( "Non-Body parameter in `{def_name}` does not specify `type`.".format( def_name=def_name ), ) if param["type"] == "array" and "items" not in param: raise SwaggerValidationError( "Non-Body array parameter in `{def_name}` does not specify `items`.".format( def_name=def_name ), ) def validate_body_parameter( param: dict[str, Any], deref: Callable, def_name: str ) -> None: if "schema" not in param: raise SwaggerValidationError( "Body parameter in `{def_name}` does not specify `schema`.".format( def_name=def_name ) ) validate_definition( definition=param["schema"], deref=deref, def_name="{}/schema".format(def_name), visited_definitions=set(), ) def validate_parameter(param: dict[str, Any], deref: Callable, def_name: str) -> None: validate_default_in_parameter(param, deref) if not is_ref(param): if param["in"] == "body": validate_body_parameter(param, deref, def_name) else: validate_non_body_parameter(param, deref, def_name) def validate_apis(apis: dict[str, Any], deref: Callable) -> None: """Validates semantic errors in #/paths. :param apis: dict of all the #/paths :param deref: callable that dereferences $refs :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` :raises: :py:class:`jsonschema.exceptions.ValidationError` """ operation_id_set = set() for api_name, api_body in apis.items(): api_body = deref(api_body) api_params = deref(api_body.get("parameters", [])) validate_duplicate_param(api_params, deref) for idx, param in enumerate(api_params): validate_parameter( param=param, deref=deref, def_name="#/paths/{api_name}/parameters/{idx}".format( api_name=api_name, idx=idx, ), ) for oper_name in api_body: # don't treat parameters that apply to all api operations as # an operation if oper_name == "parameters" or oper_name.startswith("x-"): continue oper_body = deref(api_body[oper_name]) # Check that, if this operation has an operationId defined, # no other operation also has that operationId. operation_id = oper_body.get("operationId") if operation_id is not None: if operation_id in operation_id_set: raise SwaggerValidationError( "Duplicate operationId: {}".format(operation_id) ) operation_id_set.add(operation_id) oper_params = deref(oper_body.get("parameters", [])) validate_duplicate_param(oper_params, deref) all_path_params = list( set( get_path_param_names(api_params, deref) + get_path_param_names(oper_params, deref), ) ) validate_unresolvable_path_params(api_name, all_path_params) for idx, param in enumerate(oper_params): validate_parameter( param=param, deref=deref, def_name="#/paths/{api_name}/{oper_name}/parameters/{idx}".format( api_name=api_name, oper_name=oper_name, idx=idx, ), ) # Responses validation validate_responses(api_name, oper_name, oper_body["responses"], deref) def get_collapsed_properties_type_mappings( definition: dict[str, Any], deref: Callable ) -> tuple[dict[Any, Any], dict[Any, Any]]: """ Get all the properties for a swagger model (definition). :param definition: dictionary representation of the definition :type definition: dict :param deref: callable that dereferences $refs :return: (required properties type mapping, not required properties type mapping) :type: tuple """ definition = deref(definition) required_properties = {} not_required_properties = {} if definition.get("allOf"): for inner_definition in definition["allOf"]: ( inner_required_properties, inner_not_required_properties, ) = get_collapsed_properties_type_mappings(inner_definition, deref) required_properties.update(inner_required_properties) not_required_properties.update(inner_not_required_properties) else: properties = { prop_name: prop_schema.get("type", "object") for prop_name, prop_schema in definition.get("properties", {}).items() } required_properties_set = set(definition.get("required", [])) for k, v in properties.items(): if k in required_properties_set: required_properties[k] = v else: not_required_properties[k] = v return required_properties, not_required_properties def validate_property_default(property_spec: dict[str, Any], deref: Callable) -> None: """ Validates that default values for definitions are of the property type. Enforces presence of "type" in case of deffefwf"default" presence. :param property_spec: schema object (#/definitions//properties/ :param deref: callable that dereferences $refs :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ deref_property_spec = deref(property_spec) if "default" in deref_property_spec: if ( deref_property_spec["default"] is None and deref_property_spec.get("x-nullable", False) is True ): # In case x-nullable property is set to true, null is a valid default return validate_value_type( schema=property_spec, value=deref_property_spec["default"], deref=deref ) def validate_defaults_in_definition( definition_spec: dict[str, Any], deref: Callable ) -> None: for property_name, property_spec in (definition_spec.get("properties", {})).items(): validate_property_default(property_spec, deref) def validate_arrays_in_definition( definition_spec: dict[str, Any], deref: Callable, def_name: str | None = None, visited_definitions: set[str] | None = None, ) -> None: if definition_spec.get("type") == "array": if "items" not in definition_spec: raise SwaggerValidationError( "Definition of type array must define `items` property{}.".format( "" if not def_name else " (definition {})".format(def_name), ), ) validate_definition( definition=definition_spec["items"], deref=deref, def_name="{}/items".format(def_name), visited_definitions=visited_definitions, ) def validate_definition( definition: dict[str, Any], deref: Callable, def_name: str | None = None, visited_definitions: set[str] | None = None, ) -> None: """ :param visited_definitions: set of already visited definitions This is used to cut recursion in case of recursive definitions :type visited_definitions: set """ if visited_definitions is not None: # Remove x-scope or else no two definitions will be the same stripped_definition = json.dumps( {key: definition[key] for key in definition if key != "x-scope"}, sort_keys=True, ) if stripped_definition in visited_definitions: return visited_definitions.add(stripped_definition) definition = deref(definition) swagger_type = definition.get("type") if isinstance(swagger_type, list): # not valid Swagger; see https://github.com/OAI/OpenAPI-Specification/issues/458 raise SwaggerValidationError( "In definition of {}, type must be a string; lists are not allowed ({})".format( def_name or "(no name)", swagger_type ) ) if "allOf" in definition: for idx, inner_definition in enumerate(definition["allOf"]): validate_definition( definition=inner_definition, deref=deref, def_name="{}/{}".format(def_name, str(idx)), visited_definitions=visited_definitions, ) else: required = definition.get("required", []) props = definition.get("properties", {}).keys() extra_props = list(set(required) - set(props)) if extra_props: raise SwaggerValidationError( "In definition of {}, required list has properties not defined: {}.".format( def_name or "(no name)", extra_props, ) ) validate_defaults_in_definition(definition, deref) validate_arrays_in_definition( definition_spec=definition, deref=deref, def_name=def_name, visited_definitions=visited_definitions, ) for property_name, property_spec in definition.get("properties", {}).items(): validate_definition( definition=property_spec, deref=deref, def_name="{}/properties/{}".format(def_name, property_name), visited_definitions=visited_definitions, ) if "additionalProperties" in definition: if definition.get("additionalProperties") not in (True, False): validate_definition( definition=cast("dict[str, Any]", definition["additionalProperties"]), deref=deref, def_name="{}/additionalProperties".format(def_name), visited_definitions=visited_definitions, ) if "discriminator" in definition: required_props, not_required_props = get_collapsed_properties_type_mappings( definition, deref ) discriminator = definition["discriminator"] if ( discriminator not in required_props and discriminator not in not_required_props ): raise SwaggerValidationError( "In definition of {}, discriminator ({}) must be defined in properties".format( def_name or "(no name)", discriminator ) ) if discriminator not in required_props: raise SwaggerValidationError( "In definition of {}, discriminator ({}) must be a required property".format( def_name or "(no name)", discriminator ) ) if required_props[discriminator] != "string": raise SwaggerValidationError( "In definition of {}, discriminator ({}) must be a string property".format( def_name or "(no name)", discriminator ) ) def validate_definitions(definitions: dict[str, Any], deref: Callable) -> None: """Validates the semantic errors in #/definitions. :param definitions: dict of all the definitions :param deref: callable that dereferences $refs :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` :raises: :py:class:`jsonschema.exceptions.ValidationError` """ visited_definitions: set[str] = set() for def_name, definition in definitions.items(): validate_definition( definition=definition, deref=deref, def_name="#/definitions/{}".format(def_name), visited_definitions=visited_definitions, ) def validate_parameters(parameters: dict[str, Any], deref: Callable) -> None: for param_name, param_spec in parameters.items(): validate_parameter( param=param_spec, deref=deref, def_name="#/parameters/{}".format(param_name), ) def get_path_param_names(params: list[Any], deref: Callable) -> list[str]: """Fetch all the names of the path parameters of an operation. :param params: list of all the params :param deref: callable that dereferences $refs :returns: list of the name of the path params """ return [deref(param)["name"] for param in params if deref(param)["in"] == "path"] def validate_duplicate_param(params: list[Any], deref: Callable) -> None: """Validate no duplicate parameters are present. Uniqueness is determined by the tuple ('name', 'in'). :param params: list of all the params :param deref: callable that dereferences $refs :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` when a duplicate parameter is found. """ seen = set() msg = "Duplicate param found with (name, in)" for param in params: param = deref(param) param_key = (param["name"], param["in"]) if param_key in seen: raise SwaggerValidationError("{}: {}".format(msg, param_key)) seen.add(param_key) def get_path_params_from_url(path: str) -> list[str]: """Parse the path parameters from a path string :param path: path url to parse for parameters :returns: List of path parameter names """ formatter = string.Formatter() return [item[1] for item in formatter.parse(path) if item[1]] def validate_unresolvable_path_params(path_name: str, path_params: list[str]) -> None: """Validate that every path parameter listed is also defined. :param path_name: complete path name as a string. :param path_params: Names of all the eligible path parameters :raises: :py:class:`swagger_spec_validator.SwaggerValidationError` """ for path in get_path_params_from_url(path_name): if path not in path_params: msg = "Path parameter '{}' used is not documented on '{}'".format( path, path_name ) raise SwaggerValidationError(msg) def is_ref(spec_dict: dict[Any, Any] | list[Any]) -> bool: return isinstance(spec_dict, dict) and isinstance(spec_dict.get("$ref"), str) swagger_spec_validator-3.0.3/tests/000077500000000000000000000000001433033312100173665ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/__init__.py000066400000000000000000000001101433033312100214670ustar00rootroot00000000000000import os TESTS_BASE_PATH = os.path.abspath(os.path.dirname(__file__)) swagger_spec_validator-3.0.3/tests/common_test.py000066400000000000000000000011551433033312100222710ustar00rootroot00000000000000import uuid from unittest import mock from pkg_resources import resource_filename from swagger_spec_validator.common import read_file from swagger_spec_validator.common import read_resource_file def test_read_file(): read_file("./tests/data/v2.0/petstore.json") def test_read_resource_file(monkeypatch): resource_path = str(uuid.uuid4()) + ".json" with mock.patch("swagger_spec_validator.common.read_file") as m: read_resource_file(resource_path) read_resource_file(resource_path) m.assert_called_once_with( resource_filename("swagger_spec_validator", resource_path) ) swagger_spec_validator-3.0.3/tests/conftest.py000066400000000000000000000017671433033312100216000ustar00rootroot00000000000000import os import pytest def is_urlopen_error(exception): return any( urlopen_error_str in str(exception) for urlopen_error_str in { "", "", "", "", } ) @pytest.fixture(autouse=True) def change_dir(): """ Change the base directory to git root directory such that tests does not need to generate the absolute path for the data files. A relative path from git top-level directory will work as well """ current_directory = os.path.abspath(os.curdir) try: os.chdir(os.path.abspath(os.path.dirname(os.path.dirname(__file__)))) yield finally: os.chdir(current_directory) swagger_spec_validator-3.0.3/tests/data/000077500000000000000000000000001433033312100202775ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v1.2/000077500000000000000000000000001433033312100207655ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/000077500000000000000000000000001433033312100242665ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/array_nested_fail.json000066400000000000000000000012151433033312100306330ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "method": "GET", "nickname": "foo", "parameters": [], "type": "bar" } ], "path": "/foo" } ], "basePath": "http://localhost", "models": { "bar": { "id": "bar", "properties": { "baz": { "items": { "type": "array" }, "type": "array" } } } }, "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/array_pass.json000066400000000000000000000012161433033312100273250ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "method": "GET", "nickname": "foo", "parameters": [], "type": "bar" } ], "path": "/foo" } ], "basePath": "http://localhost", "models": { "bar": { "id": "bar", "properties": { "baz": { "items": { "type": "string" }, "type": "array" } } } }, "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/basic_pass.json000066400000000000000000000011021433033312100272620ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "method": "GET", "nickname": "foo", "parameters": [ { "name": "name", "paramType": "query", "type": "string" } ], "type": "string" } ], "path": "/foo" } ], "basePath": "http://localhost", "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/format_fail.json000066400000000000000000000006241433033312100274460ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "format": "byte", "method": "GET", "nickname": "foo", "parameters": [], "type": "number" } ], "path": "/foo" } ], "basePath": "http://localhost", "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/format_pass.json000066400000000000000000000006251433033312100275020ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "format": "float", "method": "GET", "nickname": "foo", "parameters": [], "type": "number" } ], "path": "/foo" } ], "basePath": "http://localhost", "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/ref_in_model_pass.json000066400000000000000000000013351433033312100306330ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "method": "GET", "nickname": "foo", "parameters": [], "type": "bar" } ], "path": "/foo" } ], "basePath": "http://localhost", "models": { "bar": { "id": "bar", "properties": { "baz": { "$ref": "qux" } } }, "qux": { "id": "qux", "properties": { "quux": { "type": "string" } } } }, "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/ref_in_operation_fail.json000066400000000000000000000010601433033312100314730ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "$ref": "bar", "method": "GET", "nickname": "foo", "parameters": [] } ], "path": "/foo" } ], "basePath": "http://localhost", "models": { "bar": { "id": "bar", "properties": { "baz": { "type": "string" } } } }, "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/ref_in_parameter_fail.json000066400000000000000000000014031433033312100314540ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "method": "GET", "nickname": "foo", "parameters": [ { "$ref": "baz", "name": "bar", "paramType": "query" } ], "type": "string" } ], "path": "/foo" } ], "basePath": "http://localhost", "models": { "baz": { "id": "baz", "properties": { "qux": { "type": "string" } } } }, "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/type_in_model_fail.json000066400000000000000000000013351433033312100310050ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "method": "GET", "nickname": "foo", "parameters": [], "type": "bar" } ], "path": "/foo" } ], "basePath": "http://localhost", "models": { "bar": { "id": "bar", "properties": { "baz": { "type": "qux" } } }, "qux": { "id": "qux", "properties": { "quux": { "type": "string" } } } }, "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/api_declarations/type_unknown_fail.json000066400000000000000000000005571433033312100307230ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "method": "GET", "nickname": "foo", "parameters": [], "type": "unknown" } ], "path": "/foo" } ], "basePath": "http://localhost", "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/foo/000077500000000000000000000000001433033312100215505ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v1.2/foo/foo.json000066400000000000000000000016561433033312100232360ustar00rootroot00000000000000{ "apis": [ { "operations": [ { "items": { "$ref": "FooResponse" }, "method": "GET", "nickname": "foo", "parameters": [ { "name": "name", "paramType": "query", "type": "string" } ], "type": "array" } ], "path": "/foo" } ], "basePath": "http://localhost", "models": { "FooResponse": { "id": "FooResponse", "properties": { "message": { "description": "A message", "type": "string" } } } }, "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/foo/swagger_api.json000066400000000000000000000002231433033312100247300ustar00rootroot00000000000000{ "apis": [ { "description": "Some description", "path": "/foo" } ], "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v1.2/resource_listings/000077500000000000000000000000001433033312100245305ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v1.2/resource_listings/basic_pass.json000066400000000000000000000002231433033312100275270ustar00rootroot00000000000000{ "apis": [ { "description": "Some description", "path": "/foo" } ], "swaggerVersion": "1.2" } swagger_spec_validator-3.0.3/tests/data/v2.0/000077500000000000000000000000001433033312100207645ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/instagram.json000066400000000000000000001445211433033312100236530ustar00rootroot00000000000000{ "basePath": "/v1", "consumes": [ "application/json" ], "definitions": { "Comment": { "properties": { "created_time": { "type": "string" }, "from": { "$ref": "#/definitions/MiniProfile" }, "id": { "type": "string" }, "text": { "type": "string" } }, "type": "object" }, "Image": { "properties": { "height": { "type": "integer" }, "url": { "type": "string" }, "width": { "type": "integer" } } }, "Like": { "properties": { "first_name": { "type": "string" }, "id": { "type": "string" }, "last_name": { "type": "string" }, "type": { "type": "string" }, "user_name": { "type": "string" } }, "type": "object" }, "Location": { "properties": { "id": { "type": "string" }, "latitude": { "type": "number" }, "longitude": { "type": "number" }, "name": { "type": "string" } }, "type": "object" }, "Media": { "properties": { "comments:": { "properties": { "count": { "type": "integer" }, "data": { "items": { "$ref": "#/definitions/Comment" }, "type": "array" } }, "type": "object" }, "created_time": { "description": "Epoc time (ms)", "type": "integer" }, "filter": { "type": "string" }, "id": { "type": "integer" }, "images": { "properties": { "low_resolution": { "$ref": "#/definitions/Image" }, "standard_resolution": { "$ref": "#/definitions/Image" }, "thumbnail": { "$ref": "#/definitions/Image" } } }, "likes": { "properties": { "count": { "type": "integer" }, "data": { "items": { "$ref": "#/definitions/MiniProfile" }, "type": "array" } }, "type": "object" }, "location": { "$ref": "#/definitions/Location" }, "tags": { "items": { "$ref": "#/definitions/Tag" }, "type": "array" }, "type": { "type": "string" }, "user": { "$ref": "#/definitions/MiniProfile" }, "users_in_photo": { "items": { "$ref": "#/definitions/MiniProfile" }, "type": "array" }, "videos": { "properties": { "low_resolution": { "$ref": "#/definitions/Image" }, "standard_resolution": { "$ref": "#/definitions/Image" } } } }, "type": "object" }, "MiniProfile": { "description": "A shorter version of User for likes array", "properties": { "full_name": { "type": "string" }, "id": { "type": "integer" }, "profile_picture": { "type": "string" }, "user_name": { "type": "string" } } }, "Tag": { "properties": { "media_count": { "type": "integer" }, "name": { "type": "string" } }, "type": "object" }, "User": { "properties": { "bio": { "type": "string" }, "counts": { "properties": { "follows": { "type": "integer" }, "follwed_by": { "type": "integer" }, "media": { "type": "integer" } }, "type": "object" }, "full_name": { "type": "string" }, "id": { "type": "integer" }, "profile_picture": { "type": "string" }, "username": { "type": "string" }, "website": { "type": "string" } }, "type": "object" } }, "host": "api.instagram.com", "info": { "description": "The first version of the Instagram API is an exciting step forward towards\nmaking it easier for users to have open access to their data. We created it\nso that you can surface the amazing content Instagram users share every\nsecond, in fun and innovative ways.\n\nBuild something great!\n\nOnce you've\n[registered your client](http://instagram.com/developer/register/) it's easy\nto start requesting data from Instagram.\n\nAll endpoints are only accessible via https and are located at\n`api.instagram.com`. For instance: you can grab the most popular photos at\nthe moment by accessing the following URL with your client ID\n(replace CLIENT-ID with your own):\n```\n https://api.instagram.com/v1/media/popular?client_id=CLIENT-ID\n```\nYou're best off using an access_token for the authenticated user for each\nendpoint, though many endpoints don't require it.\nIn some cases an access_token will give you more access to information, and\nin all cases, it means that you are operating under a per-access_token limit\nvs. the same limit for your single client_id.\n\n\n## Limits\nBe nice. If you're sending too many requests too quickly, we'll send back a\n`503` error code (server unavailable).\nYou are limited to 5000 requests per hour per `access_token` or `client_id`\noverall. Practically, this means you should (when possible) authenticate\nusers so that limits are well outside the reach of a given user.\n\n## Deleting Objects\nWe do our best to have all our URLs be\n[RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer).\nEvery endpoint (URL) may support one of four different http verbs. GET\nrequests fetch information about an object, POST requests create objects,\nPUT requests update objects, and finally DELETE requests will delete\nobjects.\n\nSince many old browsers don't support PUT or DELETE, we've made it easy to\nfake PUTs and DELETEs. All you have to do is do a POST with _method=PUT or\n_method=DELETE as a parameter and we will treat it as if you used PUT or\nDELETE respectively.\n\n## Structure\n\n### The Envelope\nEvery response is contained by an envelope. That is, each response has a\npredictable set of keys with which you can expect to interact:\n```json\n{\n \"meta\": {\n \"code\": 200\n },\n \"data\": {\n ...\n },\n \"pagination\": {\n \"next_url\": \"...\",\n \"next_max_id\": \"13872296\"\n }\n}\n```\n\n#### META\nThe meta key is used to communicate extra information about the response to\nthe developer. If all goes well, you'll only ever see a code key with value\n200. However, sometimes things go wrong, and in that case you might see a\nresponse like:\n```json\n{\n \"meta\": {\n \"error_type\": \"OAuthException\",\n \"code\": 400,\n \"error_message\": \"...\"\n }\n}\n```\n\n#### DATA\nThe data key is the meat of the response. It may be a list or dictionary,\nbut either way this is where you'll find the data you requested.\n#### PAGINATION\nSometimes you just can't get enough. For this reason, we've provided a\nconvenient way to access more data in any request for sequential data.\nSimply call the url in the next_url parameter and we'll respond with the\nnext set of data.\n```json\n{\n ...\n \"pagination\": {\n \"next_url\": \"https://api.instagram.com/v1/tags/puppy/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&max_id=13872296\",\n \"next_max_id\": \"13872296\"\n }\n}\n```\nOn views where pagination is present, we also support the \"count\" parameter.\nSimply set this to the number of items you'd like to receive. Note that the\ndefault values should be fine for most applications - but if you decide to\nincrease this number there is a maximum value defined on each endpoint.\n\n### JSONP\nIf you're writing an AJAX application, and you'd like to wrap our response\nwith a callback, all you have to do is specify a callback parameter with\nany API call:\n```\nhttps://api.instagram.com/v1/tags/coffee/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&callback=callbackFunction\n```\nWould respond with:\n```js\ncallbackFunction({\n ...\n});\n```\n", "termsOfService": "http://instagram.com/about/legal/terms/api", "title": "Instagram API", "version": "v1" }, "parameters": { "tag-name": { "description": "Tag name", "in": "path", "name": "tag-name", "required": true, "type": "string" }, "user-id": { "description": "The user identifier number", "in": "path", "name": "user-id", "required": true, "type": "number" } }, "paths": { "/geographies/{geo-id}/media/recent": { "get": { "description": "Get recent media from a geography subscription that you created.\n**Note**: You can only access Geographies that were explicitly created\nby your OAuth client. Check the Geography Subscriptions section of the\n[real-time updates page](https://instagram.com/developer/realtime/).\nWhen you create a subscription to some geography\nthat you define, you will be returned a unique geo-id that can be used\nin this query. To backfill photos from the location covered by this\ngeography, use the [media search endpoint\n](https://instagram.com/developer/endpoints/media/).\n", "parameters": [ { "description": "Max number of media to return.", "in": "query", "name": "count", "type": "integer" }, { "description": "Return media before this `min_id`.", "in": "query", "name": "min_id", "type": "integer" } ], "responses": { "200": { "description": "OK" } } }, "parameters": [ { "description": "Geolocation ID", "in": "path", "name": "geo-id", "required": true, "type": "integer" } ] }, "/locations/search": { "get": { "description": "Search for a location by geographic coordinate.", "parameters": [ { "description": "Default is 1000m (distance=1000), max distance is 5000.", "in": "query", "name": "distance", "type": "integer" }, { "description": "Returns a location mapped off of a Facebook places id. If used, a\nFoursquare id and lat, lng are not required.\n", "in": "query", "name": "facebook_places_id", "type": "integer" }, { "description": "returns a location mapped off of a foursquare v1 api location id.\nIf used, you are not required to use lat and lng. Note that this\nmethod is deprecated; you should use the new foursquare IDs with V2\nof their API.\n", "in": "query", "name": "foursquare_id", "type": "integer" }, { "description": "atitude of the center search coordinate. If used, lng is required.\n", "in": "query", "name": "lat", "type": "number" }, { "description": "ongitude of the center search coordinate. If used, lat is required.\n", "in": "query", "name": "lng", "type": "number" }, { "description": "Returns a location mapped off of a foursquare v2 api location id. If\nused, you are not required to use lat and lng.\n", "in": "query", "name": "foursquare_v2_id", "type": "integer" } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Location" }, "type": "array" } }, "type": "object" } } }, "tags": [ "Location" ] } }, "/locations/{location-id}": { "get": { "description": "Get information about a location.", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "$ref": "#/definitions/Location" } }, "type": "object" } } }, "tags": [ "Location" ] }, "parameters": [ { "description": "Location ID", "in": "path", "name": "location-id", "required": true, "type": "integer" } ] }, "/locations/{location-id}/media/recent": { "get": { "description": "Get a list of recent media objects from a given location.", "parameters": [ { "description": "Return media before this UNIX timestamp.", "in": "query", "name": "max_timestamp", "type": "integer" }, { "description": "Return media after this UNIX timestamp.", "in": "query", "name": "min_timestamp", "type": "integer" }, { "description": "Return media later than this min_id.", "in": "query", "name": "min_id", "type": "string" }, { "description": "Return media earlier than this max_id.", "in": "query", "name": "max_id", "type": "string" } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Media" }, "type": "array" } }, "type": "object" } } }, "tags": [ "Location", "Media" ] }, "parameters": [ { "description": "Location ID", "in": "path", "name": "location-id", "required": true, "type": "integer" } ] }, "/media/popular": { "get": { "description": "Get a list of what media is most popular at the moment.\nCan return mix of image and video types.\n", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Media" }, "type": "array" } }, "type": "object" } } }, "tags": [ "Media" ] } }, "/media/search": { "get": { "description": "Search for media in a given area. The default time span is set to 5\ndays. The time span must not exceed 7 days. Defaults time stamps cover\nthe last 5 days. Can return mix of image and video types.\n", "parameters": [ { "description": "Latitude of the center search coordinate. If used, lng is required.\n", "in": "query", "name": "LAT", "type": "number" }, { "description": "A unix timestamp. All media returned will be taken later than\nthis timestamp.\n", "in": "query", "name": "MIN_TIMESTAMP", "type": "integer" }, { "description": "Longitude of the center search coordinate. If used, lat is required.\n", "in": "query", "name": "LNG", "type": "number" }, { "description": "A unix timestamp. All media returned will be taken earlier than this\ntimestamp.\n", "in": "query", "name": "MAX_TIMESTAMP", "type": "integer" }, { "default": 1000, "description": "Default is 1km (distance=1000), max distance is 5km.", "in": "query", "maximum": 5000, "name": "DISTANCE", "type": "integer" } ], "responses": { "200": { "description": "OK", "schema": { "description": "List of all media with added `distance` property", "properties": { "data": { "items": { "allOf": [ { "$ref": "#/definitions/Media" }, { "properties": { "distance": { "type": "number" } } } ] }, "type": "array" } }, "type": "object" } } }, "tags": [ "Media" ] } }, "/media/{media-id}": { "get": { "description": "Get information about a media object.\nThe returned type key will allow you to differentiate between `image`\nand `video` media.\n\nNote: if you authenticate with an OAuth Token, you will receive the\n`user_has_liked` key which quickly tells you whether the current user\nhas liked this media item.\n", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/Media" } } }, "tags": [ "Media" ] }, "parameters": [ { "description": "The media ID", "in": "path", "name": "media-id", "required": true, "type": "integer" } ] }, "/media/{media-id}/comments": { "delete": { "description": "Remove a comment either on the authenticated user's media object or\nauthored by the authenticated user.\n", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "type": "object" }, "meta": { "properties": { "code": { "type": "number" } } } }, "type": "object" } } }, "tags": [ "Comments" ] }, "get": { "description": "Get a list of recent comments on a media object.\n", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Comment" }, "type": "array" }, "meta": { "properties": { "code": { "type": "number" } } } } } } }, "tags": [ "Comments" ] }, "parameters": [ { "description": "Media ID", "in": "path", "name": "media-id", "required": true, "type": "integer" } ], "post": { "description": "Create a comment on a media object with the following rules:\n\n* The total length of the comment cannot exceed 300 characters.\n* The comment cannot contain more than 4 hashtags.\n* The comment cannot contain more than 1 URL.\n* The comment cannot consist of all capital letters.\n", "parameters": [ { "description": "Text to post as a comment on the media object as specified in\nmedia-id.\n", "in": "body", "name": "TEXT", "schema": { "type": "number" } } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "type": "object" }, "meta": { "properties": { "code": { "type": "number" } } } }, "type": "object" } } }, "security": [ { "oauth": [ "comments" ] } ], "tags": [ "Comments", "Media" ] } }, "/media/{media-id}/likes": { "delete": { "description": "Remove a like on this media by the currently authenticated user.\n", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "type": "object" }, "meta": { "properties": { "code": { "type": "number" } } } }, "type": "object" } } }, "tags": [ "Likes" ] }, "get": { "description": "Get a list of users who have liked this media.\n", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Like" }, "type": "array" }, "meta": { "properties": { "code": { "type": "number" } } } } } } }, "tags": [ "Likes", "Media" ] }, "parameters": [ { "description": "Media ID", "in": "path", "name": "media-id", "required": true, "type": "integer" } ], "post": { "description": "Set a like on this media by the currently authenticated user.", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "type": "object" }, "meta": { "properties": { "code": { "type": "number" } } } }, "type": "object" } } }, "security": [ { "oauth": [ "comments" ] } ], "tags": [ "Likes" ] } }, "/media1/{shortcode}": { "get": { "description": "This endpoint returns the same response as **GET** `/media/media-id`.\n\nA media object's shortcode can be found in its shortlink URL.\nAn example shortlink is `http://instagram.com/p/D/`\nIts corresponding shortcode is D.\n", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/Media" } } }, "tags": [ "Media" ] }, "parameters": [ { "description": "The media shortcode", "in": "path", "name": "shortcode", "required": true, "type": "string" } ] }, "/tags/search": { "get": { "parameters": [ { "description": "A valid tag name without a leading #. (eg. snowy, nofilter)\n", "in": "query", "name": "q", "type": "string" } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Tag" }, "type": "array" }, "meta": { "properties": { "code": { "type": "integer" } } } }, "type": "object" } } }, "tags": [ "Tags" ] } }, "/tags/{tag-name}": { "get": { "description": "Get information about a tag object.", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/Tag" } } }, "tags": [ "Tags" ] }, "parameters": [ { "$ref": "#/parameters/tag-name" } ] }, "/tags/{tag-name}/media/recent": { "get": { "description": "Get a list of recently tagged media. Use the `max_tag_id` and\n`min_tag_id` parameters in the pagination response to paginate through\nthese objects.\n", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Tag" }, "type": "array" } } } } }, "tags": [ "Tags" ] }, "parameters": [ { "$ref": "#/parameters/tag-name" } ] }, "/users/search": { "get": { "description": "Search for a user by name.", "parameters": [ { "description": "A query string", "in": "query", "name": "q", "required": true, "type": "string" }, { "description": "Number of users to return.", "in": "query", "name": "count", "type": "string" } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/MiniProfile" }, "type": "array" } }, "type": "object" } } }, "tags": [ "Users" ] } }, "/users/self/feed": { "get": { "description": "See the authenticated user's feed.", "parameters": [ { "description": "Count of media to return.", "in": "query", "name": "count", "type": "integer" }, { "description": "Return media earlier than this max_id.s", "in": "query", "name": "max_id", "type": "integer" }, { "description": "Return media later than this min_id.", "in": "query", "name": "min_id", "type": "integer" } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Media" }, "type": "array" } }, "type": "object" } } }, "tags": [ "Users" ] } }, "/users/self/media/liked": { "get": { "description": "See the list of media liked by the authenticated user.\nPrivate media is returned as long as the authenticated user\nhas permissionto view that media. Liked media lists are only\navailable for the currently authenticated user.\n", "parameters": [ { "description": "Count of media to return.", "in": "query", "name": "count", "type": "integer" }, { "description": "Return media liked before this id.", "in": "query", "name": "max_like_id", "type": "integer" } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Media" }, "type": "array" } }, "type": "object" } } }, "tags": [ "Users" ] } }, "/users/self/requested-by": { "get": { "description": "List the users who have requested this user's permission to follow.\n", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/MiniProfile" }, "type": "array" }, "meta": { "properties": { "code": { "type": "integer" } } } } } } }, "tags": [ "Relationships" ] } }, "/users/{user-id}": { "get": { "description": "Get basic information about a user.", "responses": { "200": { "description": "The user object", "schema": { "properties": { "data": { "$ref": "#/definitions/User" } }, "type": "object" } } }, "security": [ { "key": [] }, { "oauth": [ "basic" ] } ], "tags": [ "Users" ] }, "parameters": [ { "$ref": "#/parameters/user-id" } ] }, "/users/{user-id}/followed-by": { "get": { "description": "Get the list of users this user is followed by.", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/MiniProfile" }, "type": "array" } } } } }, "tags": [ "Relationships" ] }, "parameters": [ { "$ref": "#/parameters/user-id" } ] }, "/users/{user-id}/follows": { "get": { "description": "Get the list of users this user follows.", "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/MiniProfile" }, "type": "array" } } } } }, "tags": [ "Relationships" ] }, "parameters": [ { "$ref": "#/parameters/user-id" } ] }, "/users/{user-id}/media/recent": { "get": { "parameters": [ { "description": "Count of media to return.", "in": "query", "name": "count", "type": "integer" }, { "description": "Return media before this UNIX timestamp.", "in": "query", "name": "max_timestamp", "type": "integer" }, { "description": "Return media after this UNIX timestamp.", "in": "query", "name": "min_timestamp", "type": "integer" }, { "description": "Return media later than this min_id.", "in": "query", "name": "min_id", "type": "string" }, { "description": "Return media earlier than this max_id.", "in": "query", "name": "max_id", "type": "string" } ], "responses": { "200": { "description": "Get the most recent media published by a user. To get the most recent\nmedia published by the owner of the access token, you can use `self`\ninstead of the `user-id`.\n", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/Media" }, "type": "array" } }, "type": "object" } } }, "tags": [ "Users" ] }, "parameters": [ { "$ref": "#/parameters/user-id" } ] }, "/users/{user-id}/relationship": { "parameters": [ { "$ref": "#/parameters/user-id" } ], "post": { "description": "Modify the relationship between the current user and thetarget user.\n", "parameters": [ { "description": "One of follow/unfollow/block/unblock/approve/ignore.", "in": "body", "name": "action", "schema": { "enum": [ "follow", "unfollow", "block", "unblock", "approve" ], "type": "string" } } ], "responses": { "200": { "description": "OK", "schema": { "properties": { "data": { "items": { "$ref": "#/definitions/MiniProfile" }, "type": "array" } } } } }, "security": [ { "oauth": [ "relationships" ] } ], "tags": [ "Relationships" ] } } }, "produces": [ "application/json" ], "schemes": [ "https" ], "security": [ { "oauth": [ "basic", "comments", "relationships", "likes" ] }, { "key": [] } ], "securityDefinitions": { "key": { "in": "query", "name": "access_token", "type": "apiKey" }, "oauth": { "authorizationUrl": "https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token", "flow": "implicit", "scopes": { "basic": "to read any and all data related to a user (e.g. following/followed-by\n lists, photos, etc.) (granted by default)\n", "comments": "to create or delete comments on a user\u2019s behalf", "likes": "to like and unlike items on a user\u2019s behalf", "relationships": "to follow and unfollow users on a user\u2019s behalf" }, "type": "oauth2" } }, "swagger": "2.0", "tags": [ { "name": "Users" }, { "description": "Relationships are expressed using the following terms:\n\n**outgoing_status**: Your relationship to the user. Can be \"follows\",\n \"requested\", \"none\".\n**incoming_status**: A user's relationship to you. Can be \"followed_by\",\n \"requested_by\", \"blocked_by_you\", \"none\".\n", "name": "Relationships" }, { "description": "At this time, uploading via the API is not possible. We made a conscious\nchoice not to add this for the following reasons:\n\n* Instagram is about your life on the go \u2013 we hope to encourage photos\n from within the app.\n* We want to fight spam & low quality photos. Once we allow uploading\n from other sources, it's harder to control what comes into the Instagram\n ecosystem. All this being said, we're working on ways to ensure users\n have a consistent and high-quality experience on our platform.\n", "name": "Media" }, { "name": "Commnts" }, { "name": "Likes" }, { "name": "Tags" }, { "name": "Location" }, { "name": "Subscribtions" } ] } swagger_spec_validator-3.0.3/tests/data/v2.0/invalid_swagger_spec_because_empty_reference.yaml000066400000000000000000000004711433033312100327540ustar00rootroot00000000000000info: title: Test version: '1.0' definitions: model1: type: object x-extends: $ref: #/definitions/model2 model2: type: object paths: /endpoint: get: responses: '200': description: '' schema: $ref: '#/definitions/model1' swagger: '2.0' swagger_spec_validator-3.0.3/tests/data/v2.0/invalid_swagger_specs_because_ref_in_responses.json000066400000000000000000000005701433033312100333350ustar00rootroot00000000000000{ "info": { "title": "Test", "version": "1.0" }, "paths": { "/endpoint": { "get": { "responses": { "$ref": "#/responses" } } } }, "responses": { "default": { "description": "any response" } }, "swagger": "2.0" } swagger_spec_validator-3.0.3/tests/data/v2.0/minimal.yaml000066400000000000000000000003011433033312100232700ustar00rootroot00000000000000# http://editor2.swagger.io/spec-files/minimal.yaml --- swagger: '2.0' info: version: 0.0.0 title: Simple API paths: /: get: responses: '200': description: OK swagger_spec_validator-3.0.3/tests/data/v2.0/petstore.json000066400000000000000000001027601433033312100235320ustar00rootroot00000000000000{ "basePath": "/v2", "definitions": { "ApiResponse": { "properties": { "code": { "format": "int32", "type": "integer" }, "message": { "type": "string" }, "type": { "type": "string" } }, "type": "object" }, "Category": { "properties": { "id": { "format": "int64", "type": "integer" }, "name": { "type": "string" } }, "type": "object", "xml": { "name": "Category" } }, "Order": { "properties": { "complete": { "default": false, "type": "boolean" }, "id": { "format": "int64", "type": "integer" }, "petId": { "format": "int64", "type": "integer" }, "quantity": { "format": "int32", "type": "integer" }, "shipDate": { "format": "date-time", "type": "string" }, "status": { "description": "Order Status", "enum": [ "placed", "approved", "delivered" ], "type": "string" } }, "type": "object", "xml": { "name": "Order" } }, "Pet": { "properties": { "category": { "$ref": "#/definitions/Category" }, "id": { "format": "int64", "type": "integer" }, "name": { "example": "doggie", "type": "string" }, "photoUrls": { "items": { "type": "string" }, "type": "array", "xml": { "name": "photoUrl", "wrapped": true } }, "status": { "description": "pet status in the store", "enum": [ "available", "pending", "sold" ], "type": "string" }, "tags": { "items": { "$ref": "#/definitions/Tag" }, "type": "array", "xml": { "name": "tag", "wrapped": true } } }, "required": [ "name", "photoUrls" ], "type": "object", "xml": { "name": "Pet" } }, "Tag": { "properties": { "id": { "format": "int64", "type": "integer" }, "name": { "type": "string" } }, "type": "object", "xml": { "name": "Tag" } }, "User": { "properties": { "email": { "type": "string" }, "firstName": { "type": "string" }, "id": { "format": "int64", "type": "integer" }, "lastName": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "description": "User Status", "format": "int32", "type": "integer" }, "username": { "type": "string" } }, "type": "object", "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" }, "host": "petstore.swagger.io", "info": { "contact": { "email": "apiteam@swagger.io" }, "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" }, "termsOfService": "http://swagger.io/terms/", "title": "Swagger Petstore", "version": "1.0.0" }, "paths": { "/pet": { "post": { "consumes": [ "application/json", "application/xml" ], "description": "", "operationId": "addPet", "parameters": [ { "description": "Pet object that needs to be added to the store", "in": "body", "name": "body", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "produces": [ "application/xml", "application/json" ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "summary": "Add a new pet to the store", "tags": [ "pet" ] }, "put": { "consumes": [ "application/json", "application/xml" ], "description": "", "operationId": "updatePet", "parameters": [ { "description": "Pet object that needs to be added to the store", "in": "body", "name": "body", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "produces": [ "application/xml", "application/json" ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "summary": "Update an existing pet", "tags": [ "pet" ] } }, "/pet/findByStatus": { "get": { "description": "Multiple status values can be provided with comma seperated strings", "operationId": "findPetsByStatus", "parameters": [ { "collectionFormat": "csv", "description": "Status values that need to be considered for filter", "in": "query", "items": { "default": "available", "enum": [ "available", "pending", "sold" ], "type": "string" }, "name": "status", "required": true, "type": "array" } ], "produces": [ "application/xml", "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "items": { "$ref": "#/definitions/Pet" }, "type": "array" } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "summary": "Finds Pets by status", "tags": [ "pet" ] } }, "/pet/findByTags": { "get": { "description": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "parameters": [ { "collectionFormat": "csv", "description": "Tags to filter by", "in": "query", "items": { "type": "string" }, "name": "tags", "required": true, "type": "array" } ], "produces": [ "application/xml", "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "items": { "$ref": "#/definitions/Pet" }, "type": "array" } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "summary": "Finds Pets by tags", "tags": [ "pet" ] } }, "/pet/{petId}": { "delete": { "description": "", "operationId": "deletePet", "parameters": [ { "in": "header", "name": "api_key", "required": false, "type": "string" }, { "description": "Pet id to delete", "format": "int64", "in": "path", "name": "petId", "required": true, "type": "integer" } ], "produces": [ "application/xml", "application/json" ], "responses": { "400": { "description": "Invalid pet value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "summary": "Deletes a pet", "tags": [ "pet" ] }, "get": { "description": "Returns a single pet", "operationId": "getPetById", "parameters": [ { "description": "ID of pet to return", "format": "int64", "in": "path", "name": "petId", "required": true, "type": "integer" } ], "produces": [ "application/xml", "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ], "summary": "Find pet by ID", "tags": [ "pet" ] }, "post": { "consumes": [ "application/x-www-form-urlencoded" ], "description": "", "operationId": "updatePetWithForm", "parameters": [ { "description": "ID of pet that needs to be updated", "format": "int64", "in": "path", "name": "petId", "required": true, "type": "integer" }, { "description": "Updated name of the pet", "in": "formData", "name": "name", "required": false, "type": "string" }, { "description": "Updated status of the pet", "in": "formData", "name": "status", "required": false, "type": "string" } ], "produces": [ "application/xml", "application/json" ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "summary": "Updates a pet in the store with form data", "tags": [ "pet" ] } }, "/pet/{petId}/uploadImage": { "post": { "consumes": [ "multipart/form-data" ], "description": "", "operationId": "uploadFile", "parameters": [ { "description": "ID of pet to update", "format": "int64", "in": "path", "name": "petId", "required": true, "type": "integer" }, { "description": "Additional data to pass to server", "in": "formData", "name": "additionalMetadata", "required": false, "type": "string" }, { "description": "file to upload", "in": "formData", "name": "file", "required": false, "type": "file" } ], "produces": [ "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "summary": "uploads an image", "tags": [ "pet" ] } }, "/store/inventory": { "get": { "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "parameters": [], "produces": [ "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "additionalProperties": { "format": "int32", "type": "integer" }, "type": "object" } } }, "security": [ { "api_key": [] } ], "summary": "Returns pet inventories by status", "tags": [ "store" ] } }, "/store/order": { "post": { "description": "", "operationId": "placeOrder", "parameters": [ { "description": "order placed for purchasing the pet", "in": "body", "name": "body", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "produces": [ "application/xml", "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } }, "summary": "Place an order for a pet", "tags": [ "store" ] } }, "/store/order/{orderId}": { "delete": { "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", "operationId": "deleteOrder", "parameters": [ { "description": "ID of the order that needs to be deleted", "in": "path", "minimum": 1, "name": "orderId", "required": true, "type": "string" } ], "produces": [ "application/xml", "application/json" ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } }, "summary": "Delete purchase order by ID", "tags": [ "store" ] }, "get": { "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", "operationId": "getOrderById", "parameters": [ { "description": "ID of pet that needs to be fetched", "format": "int64", "in": "path", "maximum": 5, "minimum": 1, "name": "orderId", "required": true, "type": "integer" } ], "produces": [ "application/xml", "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } }, "summary": "Find purchase order by ID", "tags": [ "store" ] } }, "/user": { "post": { "description": "This can only be done by the logged in user.", "operationId": "createUser", "parameters": [ { "description": "Created user object", "in": "body", "name": "body", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "produces": [ "application/xml", "application/json" ], "responses": { "default": { "description": "successful operation" } }, "summary": "Create user", "tags": [ "user" ] } }, "/user/createWithArray": { "post": { "description": "", "operationId": "createUsersWithArrayInput", "parameters": [ { "description": "List of user object", "in": "body", "name": "body", "required": true, "schema": { "items": { "$ref": "#/definitions/User" }, "type": "array" } } ], "produces": [ "application/xml", "application/json" ], "responses": { "default": { "description": "successful operation" } }, "summary": "Creates list of users with given input array", "tags": [ "user" ] } }, "/user/createWithList": { "post": { "description": "", "operationId": "createUsersWithListInput", "parameters": [ { "description": "List of user object", "in": "body", "name": "body", "required": true, "schema": { "items": { "$ref": "#/definitions/User" }, "type": "array" } } ], "produces": [ "application/xml", "application/json" ], "responses": { "default": { "description": "successful operation" } }, "summary": "Creates list of users with given input array", "tags": [ "user" ] } }, "/user/login": { "get": { "description": "", "operationId": "loginUser", "parameters": [ { "description": "The user name for login", "in": "query", "name": "username", "required": true, "type": "string" }, { "description": "The password for login in clear text", "in": "query", "name": "password", "required": true, "type": "string" } ], "produces": [ "application/xml", "application/json" ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "description": "date in UTC when toekn expires", "format": "date-time", "type": "string" }, "X-Rate-Limit": { "description": "calls per hour allowed by the user", "format": "int32", "type": "integer" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } }, "summary": "Logs user into the system", "tags": [ "user" ] } }, "/user/logout": { "get": { "description": "", "operationId": "logoutUser", "parameters": [], "produces": [ "application/xml", "application/json" ], "responses": { "default": { "description": "successful operation" } }, "summary": "Logs out current logged in user session", "tags": [ "user" ] } }, "/user/{username}": { "delete": { "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "parameters": [ { "description": "The name that needs to be deleted", "in": "path", "name": "username", "required": true, "type": "string" } ], "produces": [ "application/xml", "application/json" ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } }, "summary": "Delete user", "tags": [ "user" ] }, "get": { "description": "", "operationId": "getUserByName", "parameters": [ { "description": "The name that needs to be fetched. Use user1 for testing. ", "in": "path", "name": "username", "required": true, "type": "string" } ], "produces": [ "application/xml", "application/json" ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } }, "summary": "Get user by user name", "tags": [ "user" ] }, "put": { "description": "This can only be done by the logged in user.", "operationId": "updateUser", "parameters": [ { "description": "name that need to be deleted", "in": "path", "name": "username", "required": true, "type": "string" }, { "description": "Updated user object", "in": "body", "name": "body", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "produces": [ "application/xml", "application/json" ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } }, "summary": "Updated user", "tags": [ "user" ] } } }, "schemes": [ "http" ], "securityDefinitions": { "api_key": { "in": "header", "name": "api_key", "type": "apiKey" }, "petstore_auth": { "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" }, "type": "oauth2" } }, "swagger": "2.0", "tags": [ { "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" }, "name": "pet" }, { "description": "Access to Petstore orders", "name": "store" }, { "description": "Operations about user", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" }, "name": "user" } ] } swagger_spec_validator-3.0.3/tests/data/v2.0/pingpong.json000066400000000000000000000007231433033312100235020ustar00rootroot00000000000000{ "ping": { "get": { "operationId": "ping", "parameters": [ { "description": "true or false", "in": "query", "name": "pung", "type": "boolean" } ], "responses": { "200": { "description": "Successful response" } } } } } swagger_spec_validator-3.0.3/tests/data/v2.0/relative_ref.json000066400000000000000000000004761433033312100243350ustar00rootroot00000000000000{ "info": { "title": "Simple", "version": "1.0.0" }, "paths": { "/ping": { "$ref": "pingpong.json#/ping" } }, "swagger": "2.0", "tags": [ { "description": "pingpong related specs", "name": "pingpong" } ] } swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/000077500000000000000000000000001433033312100253265ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/definitions/000077500000000000000000000000001433033312100276415ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/definitions/definitions.json000066400000000000000000000002311433033312100330430ustar00rootroot00000000000000{ "pong": { "properties": { "pang": { "type": "string" } }, "type": "object" } } swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/definitions/pet.yaml000066400000000000000000000000621433033312100313130ustar00rootroot00000000000000type: object properties: name: type: string swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/operations/000077500000000000000000000000001433033312100275115ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/operations/operations.json000066400000000000000000000011131433033312100325630ustar00rootroot00000000000000{ "ping": { "get": { "operationId": "ping", "parameters": [ { "$ref": "../parameters/parameters.json#/pung" } ], "responses": { "200": { "$ref": "../responses/responses.json#/pong" } }, "tags": [ "pingpong" ], "x-something": [ { "$ref": "../responses/responses.json#/pong" } ] } } } swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/parameters/000077500000000000000000000000001433033312100274715ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/parameters/parameters.json000066400000000000000000000002111433033312100325210ustar00rootroot00000000000000{ "pung": { "description": "true or false", "in": "query", "name": "pung", "type": "boolean" } } swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/paths/000077500000000000000000000000001433033312100264455ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/paths/paths.json000066400000000000000000000015601433033312100304610ustar00rootroot00000000000000{ "answer": { "get": { "operationId": "answer", "responses": { "200": { "description": "answer", "schema": { "$ref": "#/definitions/answer" } } } } }, "definitions": { "answer": { "properties": { "followup_question": { "$ref": "#/definitions/question" } }, "type": "object" }, "question": { "properties": { "answer": { "$ref": "#/definitions/answer" } }, "type": "object" } }, "ping": { "get": { "$ref": "../operations/operations.json#/ping/get" } } } swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/responses/000077500000000000000000000000001433033312100273475ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/responses/responses.json000066400000000000000000000002211433033312100322560ustar00rootroot00000000000000{ "pong": { "description": "pong", "schema": { "$ref": "../definitions/definitions.json#/pong" } } } swagger_spec_validator-3.0.3/tests/data/v2.0/test_complicated_refs/swagger.json000066400000000000000000000011101433033312100276510ustar00rootroot00000000000000{ "definitions": { "pet": { "$ref": "definitions/pet.yaml" }, "pong": { "$ref": "definitions/definitions.json#/pong" } }, "info": { "title": "Simple", "version": "1.0.0" }, "paths": { "/answer": { "$ref": "paths/paths.json#/answer" }, "/ping": { "$ref": "paths/paths.json#/ping" } }, "swagger": "2.0", "tags": [ { "description": "pingpong related specs", "name": "pingpong" } ] } swagger_spec_validator-3.0.3/tests/data/v2.0/test_fails_on_invalid_external_ref/000077500000000000000000000000001433033312100300615ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_fails_on_invalid_external_ref/pet.json000066400000000000000000000007321433033312100315460ustar00rootroot00000000000000{ "getall": { "get": { "operationId": "get_all_pets", "parameters": [ { "description": "`name` is missing - this should fail validation", "in": "query", "type": "string" } ], "responses": { "200": { "description": "Successful response" } } } } } swagger_spec_validator-3.0.3/tests/data/v2.0/test_fails_on_invalid_external_ref/petstore.json000066400000000000000000000004531433033312100326230ustar00rootroot00000000000000{ "info": { "title": "Petstore", "version": "1.0.0" }, "paths": { "/pet/getall": { "$ref": "pet.json#/getall" } }, "swagger": "2.0", "tags": [ { "description": "pet", "name": "pet" } ] } swagger_spec_validator-3.0.3/tests/data/v2.0/test_fails_on_invalid_external_ref_in_list/000077500000000000000000000000001433033312100316025ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_fails_on_invalid_external_ref_in_list/pet.json000066400000000000000000000002271433033312100332660ustar00rootroot00000000000000{ "parameter": { "description": "`name` is missing - this should fail validation", "in": "query", "type": "string" } } petstore.json000066400000000000000000000012141433033312100342610ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_fails_on_invalid_external_ref_in_list{ "info": { "title": "Petstore", "version": "1.0.0" }, "paths": { "/pet/getall": { "get": { "operationId": "get_all_pets", "parameters": [ { "$ref": "pet.json#/parameter" } ], "responses": { "200": { "description": "Successful response" } } } } }, "swagger": "2.0", "tags": [ { "description": "pet", "name": "pet" } ] } swagger_spec_validator-3.0.3/tests/data/v2.0/test_polymorphic_specs/000077500000000000000000000000001433033312100255655ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/data/v2.0/test_polymorphic_specs/swagger.json000066400000000000000000000066331433033312100301270ustar00rootroot00000000000000{ "basePath": "/", "definitions": { "BaseObject": { "allOf": [ { "$ref": "#/definitions/GenericPet" } ] }, "Bird": { "properties": { "name": { "type": "string" } }, "title": "Bird", "type": "object" }, "Cat": { "allOf": [ { "$ref": "#/definitions/GenericPet" }, { "properties": { "color": { "enum": [ "red", "green", "yellow", "white", "black" ], "type": "string" } } } ], "title": "Cat", "type": "object" }, "Dog": { "allOf": [ { "$ref": "#/definitions/GenericPet" }, { "properties": { "birth_date": { "format": "date", "type": "string" } }, "required": [ "birth_date" ] } ], "title": "Dog", "type": "object" }, "GenericPet": { "discriminator": "type", "properties": { "name": { "type": "string" }, "type": { "type": "string" }, "weight": { "type": "integer" } }, "required": [ "type", "weight" ], "title": "GenericPet", "type": "object" }, "PetList": { "properties": { "list": { "items": { "$ref": "#/definitions/GenericPet" }, "type": "array" }, "number_of_pets": { "type": "integer" } }, "type": "object" }, "Whale": { "allOf": [ { "$ref": "#/definitions/Bird" }, { "properties": { "weight": { "type": "integer" } } } ], "title": "Whale", "type": "object" } }, "info": { "description": "Small example of polymorphic Swaggr specs", "title": "Pets", "version": "0.0.0" }, "paths": { "/pets": { "get": { "operationId": "get_pets", "responses": { "200": { "description": "Pet List" } }, "summary": "Get all the pets" } } }, "swagger": "2.0" } swagger_spec_validator-3.0.3/tests/util/000077500000000000000000000000001433033312100203435ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/util/__init__.py000066400000000000000000000000001433033312100224420ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/util/get_validator_test.py000066400000000000000000000025311433033312100246010ustar00rootroot00000000000000import pytest from swagger_spec_validator import validator12 from swagger_spec_validator import validator20 from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.util import get_validator def test_version_missing(): with pytest.raises(SwaggerValidationError) as excinfo: get_validator({}, "http://foo.com") assert "missing" in str(excinfo.value) def test_1_dot_x_version_not_supported(): with pytest.raises(SwaggerValidationError) as excinfo: get_validator({"swaggerVersion": "0.9"}, "http://foo.com") assert "not supported" in str(excinfo.value) def test_2_dot_x_version_not_supported(): with pytest.raises(SwaggerValidationError) as excinfo: get_validator({"swagger": "1.2"}, "http://foo.com") assert "not supported" in str(excinfo.value) def test_both_swagger_1_dot_x_and_2_dot_x_version_keys_found(): with pytest.raises(SwaggerValidationError) as excinfo: spec = {"swagger": "2.0", "swaggerVersion": "1.2"} get_validator(spec, "http://foo.com") assert "not both" in str(excinfo.value) def test_success_12(): spec = {"swaggerVersion": "1.2"} assert validator12 == get_validator(spec, "http://foo.com") == validator12 def test_success_20(): spec = {"swagger": "2.0"} assert validator20 == get_validator(spec, "http://foo.com") swagger_spec_validator-3.0.3/tests/util/validate_spec_url_test.py000066400000000000000000000014251433033312100254430ustar00rootroot00000000000000from unittest import mock import pytest from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.util import validate_spec_url from tests.conftest import is_urlopen_error def test_raise_SwaggerValidationError_on_urlopen_error(): with pytest.raises(SwaggerValidationError) as excinfo: validate_spec_url("http://foo") assert is_urlopen_error(excinfo.value) @mock.patch("swagger_spec_validator.util.read_url") @mock.patch("swagger_spec_validator.util.get_validator") def test_validate_spec_url_success(mock_get_validator, mock_read_url): spec_url = mock.Mock() validate_spec_url(spec_url) mock_read_url.assert_called_once_with(spec_url) mock_get_validator.assert_called_once_with(mock_read_url.return_value, spec_url) swagger_spec_validator-3.0.3/tests/validator12/000077500000000000000000000000001433033312100215165ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/validator12/__init__.py000066400000000000000000000000001433033312100236150ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/validator12/get_resource_path_test.py000066400000000000000000000010641433033312100266320ustar00rootroot00000000000000import pytest from swagger_spec_validator.validator12 import get_resource_path def test_fetch_from_file_uri_success(): url, resource = "file://bla/bar", "/foo" assert "file://bla/foo.json", get_resource_path(url, resource) def test_fetch_from_http_uri_success(): url, resource = "http://bla/bar", "/foo" assert "http://bla/bar/foo", get_resource_path(url, resource) def test_fetch_from_bad_file_uri_fail(): with pytest.raises(AssertionError): url, resource = "file://bla/bar", "no_slash" get_resource_path(url, resource) swagger_spec_validator-3.0.3/tests/validator12/run_test.py000066400000000000000000000024571433033312100237430ustar00rootroot00000000000000import glob import json import os.path import jsonschema.exceptions import pytest from swagger_spec_validator import SwaggerValidationError from swagger_spec_validator.validator12 import validate_api_declaration from swagger_spec_validator.validator12 import validate_resource_listing def run_json_tests_with_func(json_test_paths, func): """Run the specified test function over a list of JSON test files.""" for json_test_path in sorted(json_test_paths): with open(json_test_path) as fd: test_data = json.load(fd) # Grab last two components from test_path # e.g. "api_declarations/array_nested_fail.json" test_name = os.sep.join(json_test_path.split(os.sep)[-2:]) print("Testing %s..." % test_name) if test_name.endswith("_pass.json"): func(test_data) elif test_name.endswith("_fail.json"): with pytest.raises( (SwaggerValidationError, jsonschema.exceptions.ValidationError) ): func(test_data) def test_main(): run_json_tests_with_func( glob.glob("./tests/data/v1.2/api_declarations/*.json"), validate_api_declaration ) run_json_tests_with_func( glob.glob("./tests/data/v1.2/resource_listings/*.json"), validate_resource_listing, ) swagger_spec_validator-3.0.3/tests/validator12/validate_json_test.py000066400000000000000000000013361433033312100257540ustar00rootroot00000000000000import json import pytest from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator12 import validate_json def test_success(): with open("./tests/data/v1.2/foo/swagger_api.json") as f: resource_listing = json.load(f) validate_json(resource_listing, "schemas/v1.2/resourceListing.json") with open("./tests/data/v1.2/foo/foo.json") as f: api_declaration = json.load(f) validate_json(api_declaration, "schemas/v1.2/apiDeclaration.json") def test_failure(): with pytest.raises(SwaggerValidationError) as excinfo: validate_json({}, "schemas/v1.2/apiDeclaration.json") assert "'swaggerVersion' is a required property" in str(excinfo.value) swagger_spec_validator-3.0.3/tests/validator12/validate_spec_test.py000066400000000000000000000051071433033312100257350ustar00rootroot00000000000000from unittest import mock import pytest from .validate_spec_url_test import make_mock_responses from .validate_spec_url_test import read_contents from swagger_spec_validator.common import get_uri_from_file_path from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator12 import validate_data_type from swagger_spec_validator.validator12 import validate_model from swagger_spec_validator.validator12 import validate_parameter from swagger_spec_validator.validator12 import validate_spec from tests import TESTS_BASE_PATH RESOURCE_LISTING_FILE = TESTS_BASE_PATH + "/data/v1.2/foo/swagger_api.json" API_DECLARATION_FILE = TESTS_BASE_PATH + "/data/v1.2/foo/foo.json" def get_resource_listing(): return read_contents(RESOURCE_LISTING_FILE) def test_http_success(): mock_responses = make_mock_responses([API_DECLARATION_FILE]) with mock.patch( "swagger_spec_validator.validator12.read_url", side_effect=mock_responses ) as mock_read_url: validate_spec(get_resource_listing(), "http://localhost/api-docs") mock_read_url.assert_called_once_with("http://localhost/api-docs/foo") def test_file_uri_success(): mock_string = "swagger_spec_validator.validator12.validate_api_declaration" with mock.patch(mock_string) as mock_api: validate_spec( get_resource_listing(), get_uri_from_file_path(RESOURCE_LISTING_FILE), ) expected = read_contents(API_DECLARATION_FILE) mock_api.assert_called_once_with(expected) def test_validate_parameter_type_file_in_form(): parameter = { "paramType": "form", "name": "what", "type": "File", } # lack of errors is success validate_parameter(parameter, []) def test_validate_parameter_type_file_in_body(): parameter = { "paramType": "body", "name": "what", "type": "File", } with pytest.raises( SwaggerValidationError, match='Type "File" is only valid for form parameters' ): validate_parameter(parameter, []) def test_validate_data_type_is_model(): model_id = "MyModelId" model_ids = [model_id, "OtherModelId"] obj = {"type": model_id} # lack of error is success validate_data_type(obj, model_ids, allow_refs=False) def test_validate_model_matches_id(): model = {"id": "mysupermodel"} model_name = "mymodel" model_ids = "" with pytest.raises( SwaggerValidationError, match="model name: mymodel does not match model id: mysupermodel", ): validate_model(model, model_name, model_ids) swagger_spec_validator-3.0.3/tests/validator12/validate_spec_url_test.py000066400000000000000000000032551433033312100266210ustar00rootroot00000000000000import json from unittest import mock import pytest from swagger_spec_validator.common import get_uri_from_file_path from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator12 import validate_spec_url from tests import TESTS_BASE_PATH from tests.conftest import is_urlopen_error RESOURCE_LISTING_FILE = TESTS_BASE_PATH + "/data/v1.2/foo/swagger_api.json" API_DECLARATION_FILE = TESTS_BASE_PATH + "/data/v1.2/foo/foo.json" def read_contents(file_name): with open(file_name) as f: return json.load(f) def make_mock_responses(file_names): return [read_contents(file_name) for file_name in file_names] def test_http_success(): mock_responses = make_mock_responses([RESOURCE_LISTING_FILE, API_DECLARATION_FILE]) with mock.patch( "swagger_spec_validator.validator12.read_url", side_effect=mock_responses, ) as mock_read_url: validate_spec_url("http://localhost/api-docs") mock_read_url.assert_has_calls( [ mock.call("http://localhost/api-docs"), mock.call("http://localhost/api-docs/foo"), ] ) def test_file_uri_success(): mock_string = "swagger_spec_validator.validator12.validate_api_declaration" with mock.patch(mock_string) as mock_api: validate_spec_url(get_uri_from_file_path(RESOURCE_LISTING_FILE)) expected = read_contents(API_DECLARATION_FILE) mock_api.assert_called_once_with(expected) def test_raise_SwaggerValidationError_on_urlopen_error(): with pytest.raises(SwaggerValidationError) as excinfo: validate_spec_url("http://foo") assert is_urlopen_error(excinfo.value) swagger_spec_validator-3.0.3/tests/validator20/000077500000000000000000000000001433033312100215155ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/validator20/__init__.py000066400000000000000000000000001433033312100236140ustar00rootroot00000000000000swagger_spec_validator-3.0.3/tests/validator20/conftest.py000066400000000000000000000010641433033312100237150ustar00rootroot00000000000000import json import os import pytest from swagger_spec_validator.common import get_uri_from_file_path from tests import TESTS_BASE_PATH @pytest.fixture(scope="session") def petstore_contents(): with open(TESTS_BASE_PATH + "/data/v2.0/petstore.json") as f: return f.read() @pytest.fixture def petstore_dict(petstore_contents): return json.loads(petstore_contents) def get_spec_json_and_url(rel_path): abs_path = os.path.abspath(rel_path) with open(abs_path) as f: return json.loads(f.read()), get_uri_from_file_path(abs_path) swagger_spec_validator-3.0.3/tests/validator20/deref_test.py000066400000000000000000000016021433033312100242120ustar00rootroot00000000000000from unittest.mock import Mock import pytest from jsonschema.exceptions import RefResolutionError from jsonschema.validators import RefResolver from swagger_spec_validator.validator20 import deref def test_none(): assert deref(None, Mock(spec=RefResolver)) is None def test_not_dict(): assert deref(1, Mock(spec=RefResolver)) == 1 def test_not_ref(): input = {"type": "object"} assert deref(input, Mock(spec=RefResolver)) == input def test_ref(): ref_dict = {"$ref": "#/definitions/Foo"} definitions = {"definitions": {"Foo": "bar"}} assert deref(ref_dict, RefResolver("", definitions)) == "bar" def test_ref_not_found(): ref_dict = {"$ref": "#/definitions/Foo"} definitions = {} with pytest.raises(RefResolutionError) as excinfo: deref(ref_dict, RefResolver("", definitions)) assert "Unresolvable JSON pointer" in str(excinfo.value) swagger_spec_validator-3.0.3/tests/validator20/get_collapsed_properties_map_test.py000066400000000000000000000042331433033312100310460ustar00rootroot00000000000000import functools from swagger_spec_validator.validator20 import deref from swagger_spec_validator.validator20 import get_collapsed_properties_type_mappings from swagger_spec_validator.validator20 import validate_json from tests.validator20.conftest import get_spec_json_and_url def get_deref(spec_dict): swagger_resolver = validate_json( spec_dict, "schemas/v2.0/schema.json", ) return functools.partial(deref, resolver=swagger_resolver) def test_get_collapsed_properties_type_mapping_simple_case(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) ( required_parameters, not_required_parameters, ) = get_collapsed_properties_type_mappings( definition=swagger_dict["definitions"]["GenericPet"], deref=get_deref(swagger_dict), ) assert required_parameters == {"type": "string", "weight": "integer"} assert not_required_parameters == {"name": "string"} def test_get_collapsed_properties_type_mapping_allOf_add_required_property(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) ( required_parameters, not_required_parameters, ) = get_collapsed_properties_type_mappings( definition=swagger_dict["definitions"]["Dog"], deref=get_deref(swagger_dict), ) assert required_parameters == { "type": "string", "weight": "integer", "birth_date": "string", } assert not_required_parameters == {"name": "string"} def test_get_collapsed_properties_type_mapping_allOf_add_not_required_property(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) ( required_parameters, not_required_parameters, ) = get_collapsed_properties_type_mappings( definition=swagger_dict["definitions"]["Cat"], deref=get_deref(swagger_dict), ) assert required_parameters == {"type": "string", "weight": "integer"} assert not_required_parameters == {"name": "string", "color": "string"} swagger_spec_validator-3.0.3/tests/validator20/validate_apis_test.py000066400000000000000000000234231433033312100257370ustar00rootroot00000000000000import pytest from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator20 import validate_apis from swagger_spec_validator.validator20 import validate_defaults_in_parameters RESPONSES = { "default": { "description": "random description", }, } def test_api_level_params_ok(): # Parameters defined at the API level apply to all operations within that # API. Make sure we don't treat the API level parameters as an operation # since they are peers. apis = { "/tags/{tag-name}": { "parameters": [ {"name": "tag-name", "in": "path", "type": "string", "required": True}, ], "get": { "responses": RESPONSES, }, }, } # Success == no exception thrown validate_apis(apis, lambda x: x) def test_api_level_x_hyphen_ok(): # Elements starting with "x-" should be ignored apis = { "/tags/{tag-name}": { "x-ignore-me": "DO NOT LOOK AT ME!", "get": { "parameters": [ { "name": "tag-name", "in": "path", "type": "string", } ], "responses": RESPONSES, }, } } # Success == no exception thrown validate_apis(apis, lambda x: x) @pytest.mark.parametrize( "partial_parameter_spec", [ {"type": "integer", "default": 1}, {"type": "boolean", "default": True}, {"type": "null", "default": None}, {"type": "number", "default": 2}, {"type": "number", "default": 3.4}, {"type": "object", "default": {"a_random_property": "valid"}}, {"type": "array", "items": {"type": "integer"}, "default": [5, 6, 7]}, {"type": "string", "default": ""}, {"type": "string", "default": None, "x-nullable": True}, {"type": ["number", "boolean"], "default": 8}, {"type": ["number", "boolean"], "default": False}, ], ) def test_api_check_default_succeed(partial_parameter_spec): apis = { "/api": { "get": { "parameters": [ dict({"name": "param", "in": "query"}, **partial_parameter_spec), ], "responses": RESPONSES, }, }, } # Success if no exception are raised validate_apis(apis, lambda x: x) @pytest.mark.parametrize( "partial_parameter_spec, validator, instance", [ [ {"type": "integer", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "boolean", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "null", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "number", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "object", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "array", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "string", "default": -1}, "type", -1, ], [ {"type": "string", "minLength": 100, "default": "short_string"}, "minLength", "short_string", ], [ {"type": ["number", "boolean"], "default": "not_a_number_or_boolean"}, "type", "not_a_number_or_boolean", ], ], ) def test_api_check_default_fails(partial_parameter_spec, validator, instance): apis = { "/api": { "get": { "parameters": [ dict({"name": "param", "in": "query"}, **partial_parameter_spec), ], "responses": RESPONSES, }, }, } with pytest.raises(SwaggerValidationError) as excinfo: validate_apis(apis, lambda x: x) validation_error = excinfo.value.args[1] assert validation_error.instance == instance assert validation_error.validator == validator def test_validate_defaults_in_parameters_succeed(): # Success if no exception are raised validate_defaults_in_parameters( params_spec=[{"type": "integer"}], deref=lambda x: x, ) def test_validate_defaults_in_parameters_fails(): with pytest.raises(SwaggerValidationError): validate_defaults_in_parameters( params_spec=[ {"type": "integer", "default": "wrong_type"}, ], deref=lambda x: x, ) @pytest.mark.parametrize( "apis", [ { "/api": { "get": { "operationId": "duplicateOperationId", "responses": {}, }, "post": { "operationId": "duplicateOperationId", "responses": {}, }, }, }, { "/api1": { "get": { "operationId": "duplicateOperationId", "responses": {}, }, }, "/api2": { "get": { "operationId": "duplicateOperationId", "responses": {}, }, }, }, { "/api1": { "get": { "operationId": "duplicateOperationId", "tags": ["tag1", "tag2"], "responses": {}, }, }, "/api2": { "get": { "operationId": "duplicateOperationId", "tags": ["tag1"], "responses": {}, }, }, }, ], ) def test_duplicate_operationIds_fails(apis): with pytest.raises(SwaggerValidationError) as excinfo: validate_apis(apis, lambda x: x) swagger_validation_error = excinfo.value error_message = swagger_validation_error.args[0] assert error_message == "Duplicate operationId: duplicateOperationId" @pytest.mark.parametrize( "apis", [ { "/api1": { "get": { "operationId": "duplicateOperationId", "tags": ["tag1"], "responses": {}, }, }, "/api2": { "get": { "operationId": "duplicateOperationId", "tags": ["tag2"], "responses": {}, }, }, "/api3": { "get": { "operationId": "duplicateOperationId", "responses": {}, }, }, }, ], ) def test_duplicate_operationIds_fails_if_tags_differ(apis): with pytest.raises(SwaggerValidationError) as excinfo: validate_apis(apis, lambda x: x) swagger_validation_error = excinfo.value error_message = swagger_validation_error.args[0] assert error_message == "Duplicate operationId: duplicateOperationId" def test_invalid_inline_models_in_responses_fails(): apis = { "/endpoint": { "get": { "responses": { "200": { "description": "desc", "schema": { "type": "object", "properties": {"prop": {"type": "array"}}, }, }, }, }, }, } with pytest.raises(SwaggerValidationError) as excinfo: validate_apis(apis, lambda x: x) assert ( str(excinfo.value) == "Definition of type array must define `items` property " "(definition #/paths//endpoint/get/responses/200/properties/prop)." ) def test_invalid_inline_models_in_operation_body_parameters_fails(): apis = { "/endpoint": { "get": { "parameters": [ { "in": "body", "name": "body", "schema": { "type": "object", "properties": {"prop": {"type": "array"}}, }, } ], "responses": { "200": { "description": "desc", }, }, }, }, } with pytest.raises(SwaggerValidationError) as excinfo: validate_apis(apis, lambda x: x) assert ( str(excinfo.value) == "Definition of type array must define `items` property " "(definition #/paths//endpoint/get/parameters/0/schema/properties/prop)." ) def test_invalid_inline_models_in_api_body_parameters_fails(): apis = { "/endpoint": { "parameters": [ { "in": "body", "name": "body", "schema": { "type": "object", "properties": {"prop": {"type": "array"}}, }, } ], "get": { "responses": { "200": { "description": "desc", }, }, }, }, } with pytest.raises(SwaggerValidationError) as excinfo: validate_apis(apis, lambda x: x) assert ( str(excinfo.value) == "Definition of type array must define `items` property " "(definition #/paths//endpoint/parameters/0/schema/properties/prop)." ) swagger_spec_validator-3.0.3/tests/validator20/validate_definitions_test.py000066400000000000000000000102641433033312100273150ustar00rootroot00000000000000import pytest from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator20 import validate_definitions @pytest.mark.parametrize( "property_spec", [ {"type": "integer", "default": 1}, {"type": "boolean", "default": True}, {"type": "null", "default": None}, {"type": "number", "default": 2}, {"type": "number", "default": 3.4}, {"type": "object", "default": {"a_random_property": "valid"}}, {"type": "array", "items": {"type": "integer"}, "default": [5, 6, 7]}, {"default": -1}, # if type is not defined any value is a valid value {"type": "string", "default": ""}, ], ) def test_api_check_default_succeed(property_spec): definitions = { "injected_definition": { "properties": { "property": property_spec, }, }, } # Success if no exception are raised validate_definitions(definitions, lambda x: x) @pytest.mark.parametrize( "property_spec, validator, instance", [ [ {"type": "integer", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "boolean", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "null", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "number", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "object", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "array", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "string", "default": -1}, "type", -1, ], [ {"type": "string", "minLength": 100, "default": "short_string"}, "minLength", "short_string", ], ], ) def test_api_check_default_fails(property_spec, validator, instance): definitions = { "injected_definition": { "properties": { "property": property_spec, }, }, } with pytest.raises(SwaggerValidationError) as excinfo: validate_definitions(definitions, lambda x: x) validation_error = excinfo.value.args[1] assert validation_error.instance == instance assert validation_error.validator == validator def test_multiple_types_fail(): definitions = { "definition_1": { "type": ["number", "boolean"], }, } with pytest.raises( SwaggerValidationError, match=r"In definition of .*, type must be a string; lists are not allowed \(.*\)", ) as excinfo: validate_definitions(definitions, lambda x: x) assert str(definitions["definition_1"]["type"]) in str(excinfo.value) def test_type_array_with_items_succeed_validation(): definitions = { "definition_1": { "type": "array", "items": { "type": "string", }, }, } # Success if no exception are raised validate_definitions(definitions, lambda x: x) def test_type_array_without_items_succeed_fails(): definitions = { "definition_1": { "type": "array", }, } with pytest.raises(SwaggerValidationError) as excinfo: validate_definitions(definitions, lambda x: x) assert ( str(excinfo.value) == "Definition of type array must define `items` property (definition #/definitions/definition_1)." ) def test_inline_model_is_not_valid_validation_fails(): definitions = { "definition_1": { "properties": { "property": { "type": "array", }, }, }, } with pytest.raises(SwaggerValidationError) as excinfo: validate_definitions(definitions, lambda x: x) assert ( str(excinfo.value) == "Definition of type array must define `items` property " "(definition #/definitions/definition_1/properties/property)." ) swagger_spec_validator-3.0.3/tests/validator20/validate_json_test.py000066400000000000000000000010241433033312100257450ustar00rootroot00000000000000import json import pytest from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator20 import validate_json def test_success(): with open("./tests/data/v2.0/petstore.json") as f: petstore_spec = json.load(f) validate_json(petstore_spec, "schemas/v2.0/schema.json") def test_failure(): with pytest.raises(SwaggerValidationError) as excinfo: validate_json({}, "schemas/v2.0/schema.json") assert "'swagger' is a required property" in str(excinfo.value) swagger_spec_validator-3.0.3/tests/validator20/validate_parameters_test.py000066400000000000000000000036231433033312100271460ustar00rootroot00000000000000import pytest from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator20 import validate_parameters @pytest.mark.parametrize( "top_level_parameters", [ {}, { "Offset": { "in": "query", "name": "offset", "type": "integer", }, }, { "AcceptedLanguage": { "in": "header", "name": "Accepted-Language", "type": "string", }, }, ], ) def test_valid_top_level_parameters(top_level_parameters): validate_parameters(top_level_parameters, deref=lambda x: x) @pytest.mark.parametrize( "top_level_parameters, expected_exception_string", [ [ { "Offset": { "in": "body", "name": "body", }, }, "Body parameter in `#/parameters/Offset` does not specify `schema`.", ], [ { "ParameterWithoutType": { "in": "header", "name": "a-random-parameter", }, }, "Non-Body parameter in `#/parameters/ParameterWithoutType` does not specify `type`.", ], [ { "ListOfIntegers": { "in": "header", "name": "Accepted-Language", "type": "array", }, }, "Non-Body array parameter in `#/parameters/ListOfIntegers` does not specify `items`.", ], ], ) def test_invalid_top_level_parameters(top_level_parameters, expected_exception_string): with pytest.raises(SwaggerValidationError) as excinfo: validate_parameters(top_level_parameters, deref=lambda x: x) assert str(excinfo.value) == expected_exception_string swagger_spec_validator-3.0.3/tests/validator20/validate_references_test.py000066400000000000000000000044471433033312100271310ustar00rootroot00000000000000import pytest from swagger_spec_validator.common import SwaggerValidationWarning from swagger_spec_validator.validator20 import validate_references @pytest.mark.parametrize( "raw_spec", [ {"$ref": "#/"}, {"description": "Description sibling is acceptable", "$ref": "#/"}, [{"$ref": "#/"}], ], ) def test_validate_valid_references(recwarn, raw_spec): validate_references(raw_spec=raw_spec, deref=lambda x: x) assert len(recwarn) == 0 @pytest.mark.parametrize( "raw_spec, expected_warning_messages", [ ( {"sibling-attribute": "", "$ref": "#/"}, ( 'Found "$ref: #/" with siblings that will be overwritten. ' "See https://stackoverflow.com/a/48114924 for more information. (path #)", ), ), ( [{"sibling-attribute": "", "$ref": "#/"}], ( 'Found "$ref: #/" with siblings that will be overwritten. ' "See https://stackoverflow.com/a/48114924 for more information. (path #/0)", ), ), ( {"$ref": None}, ( "Identified $ref with None value. This is usually an error, although technically it might be allowed. " "(path: #)", ), ), ( {"key": {"$ref": None}}, ( "Identified $ref with None value. This is usually an error, although technically it might be allowed. " "(path: #/key)", ), ), ( {"key": [{"sibling-attribute": 1, "$ref": None}]}, ( 'Found "$ref: None" with siblings that will be overwritten. ' "See https://stackoverflow.com/a/48114924 for more information. (path #/key/0)", "Identified $ref with None value. This is usually an error, although technically it might be allowed. " "(path: #/key/0)", ), ), ], ) def test_validate_references_to_warn(raw_spec, expected_warning_messages): with pytest.warns(SwaggerValidationWarning) as warninfo: validate_references(raw_spec=raw_spec, deref=lambda x: x) assert sorted(expected_warning_messages) == sorted( str(warning.message) for warning in warninfo.list ) swagger_spec_validator-3.0.3/tests/validator20/validate_rich_spec_test.py000066400000000000000000000063041433033312100267410ustar00rootroot00000000000000import json import pytest from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator20 import validate_spec @pytest.fixture def swagger_spec(petstore_contents): return json.loads(petstore_contents) def test_failure_on_duplicate_api_parameters(swagger_spec): param1 = {"name": "foo", "in": "query", "type": "string"} param2 = {"name": "foo", "in": "query", "type": "integer"} swagger_spec["paths"]["/pet"]["parameters"] = [param1, param2] with pytest.raises(SwaggerValidationError) as exc_info: validate_spec(swagger_spec) assert "Duplicate param found with (name, in): {}".format(("foo", "query")) in str( exc_info.value ) def test_failure_on_duplicate_operation_parameters(swagger_spec): param1 = {"name": "foo", "in": "query", "type": "string"} param2 = {"name": "foo", "in": "query", "type": "integer"} swagger_spec["paths"]["/pet"]["post"]["parameters"].extend([param1, param2]) with pytest.raises(SwaggerValidationError) as exc_info: validate_spec(swagger_spec) assert "Duplicate param found with (name, in): {}".format(("foo", "query")) in str( exc_info.value ) def test_failure_on_unresolvable_path_parameter(swagger_spec): swagger_spec["paths"]["/pet/{petId}"]["get"]["parameters"] = [] with pytest.raises(SwaggerValidationError) as exc_info: validate_spec(swagger_spec) assert "Path parameter 'petId' used is not documented on '/pet/{petId}'" in str( exc_info.value ) def test_failure_on_path_parameter_used_but_not_defined(swagger_spec): swagger_spec["paths"]["/user/{username}"]["get"]["parameters"][0]["name"] = "_" with pytest.raises(SwaggerValidationError) as exc_info: validate_spec(swagger_spec) assert ( "Path parameter 'username' used is not documented on '/user/{username}'" in str(exc_info.value) ) def test_failure_on_unresolvable_ref_of_props_required_list(swagger_spec): swagger_spec["definitions"]["Pet"]["required"].append("bla") with pytest.raises( SwaggerValidationError, match=r".*In definition of .*, required list has properties not defined: .*", ) as exc_info: validate_spec(swagger_spec) assert str(["bla"]) in str(exc_info.value) def test_failure_on_unresolvable_model_reference_from_model(swagger_spec): swagger_spec["definitions"]["Pet"]["properties"]["category"]["$ref"] = "_" with pytest.raises(SwaggerValidationError): validate_spec(swagger_spec) def test_failure_on_unresolvable_model_reference_from_param(swagger_spec): param = swagger_spec["paths"]["/pet"]["post"]["parameters"][0] param["schema"]["$ref"] = "#/definitions/bla" with pytest.raises(SwaggerValidationError): validate_spec(swagger_spec) def test_failure_on_unresolvable_model_reference_from_resp(swagger_spec): resp = swagger_spec["paths"]["/pet/findByStatus"]["get"]["responses"] resp["200"]["schema"]["items"]["$ref"] = "#/definitions/bla" with pytest.raises(SwaggerValidationError): validate_spec(swagger_spec) # TODO: Add warning validations for unused models, path parameter & responses # TODO: Add validations for cyclic model definitions swagger_spec_validator-3.0.3/tests/validator20/validate_spec_test.py000066400000000000000000000370631433033312100257420ustar00rootroot00000000000000import json import pytest from jsonschema.validators import RefResolver from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.validator20 import validate_spec from tests.validator20.conftest import get_spec_json_and_url @pytest.fixture def minimal_swagger_dict(): """Return minimal dict that respresents a swagger spec - useful as a base template. """ return { "swagger": "2.0", "info": { "title": "Test", "version": "1.0", }, "paths": {}, "definitions": {}, } def test_success(petstore_dict): assert isinstance(validate_spec(petstore_dict), RefResolver) def test_definitons_not_present_success(minimal_swagger_dict): del minimal_swagger_dict["definitions"] validate_spec(minimal_swagger_dict) def test_empty_definitions_success(minimal_swagger_dict): validate_spec(minimal_swagger_dict) def test_api_parameters_as_refs(): # Verify issue #29 - instragram.json comes from: # # http://editor.swagger.io/#/ # -> File # -> Open Example... # -> instagram.yaml # # and then export it to a json file. instagram_specs, _ = get_spec_json_and_url("./tests/data/v2.0/instagram.json") validate_spec(instagram_specs) def test_fails_on_invalid_external_ref_in_dict(): # The external ref in petstore.json is valid. # The contents of the external ref (pet.json#/getall) is not - the 'name' # key in the parameter is missing. petstore_spec, petstore_url = get_spec_json_and_url( "./tests/data/v2.0/test_fails_on_invalid_external_ref/petstore.json" ) with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(petstore_spec, petstore_url) assert "is not valid under any of the given schemas" in str(excinfo.value) def test_fails_on_invalid_external_ref_in_list(): # The external ref in petstore.json is valid. # The contents of the external ref (pet.json#/get_all_parameters) is not # - the 'name' key in the parameter is missing. petstore_spec, petstore_url = get_spec_json_and_url( "./tests/data/v2.0/test_fails_on_invalid_external_ref_in_list/petstore.json" ) with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(petstore_spec, petstore_url) assert "is not valid under any of the given schemas" in str(excinfo.value) @pytest.fixture def node_spec(): """Used in tests that have recursive $refs""" return { "type": "object", "properties": { "name": {"type": "string"}, "child": { "$ref": "#/definitions/Node", }, }, "required": ["name"], } def test_recursive_ref(minimal_swagger_dict, node_spec): minimal_swagger_dict["definitions"]["Node"] = node_spec validate_spec(minimal_swagger_dict) def test_recursive_ref_failure(minimal_swagger_dict, node_spec): minimal_swagger_dict["definitions"]["Node"] = node_spec # insert non-existent $ref node_spec["properties"]["foo"] = {"$ref": "#/definitions/Foo"} with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(minimal_swagger_dict) assert "Unresolvable JSON pointer" in str(excinfo.value) def test_complicated_refs(): # Split the swagger spec into a bunch of different json files and use # $refs all over to place to wire stuff together - see the test-data # files or this will make no sense whatsoever. file_path = "./tests/data/v2.0/test_complicated_refs/swagger.json" swagger_dict, origin_url = get_spec_json_and_url(file_path) resolver = validate_spec(swagger_dict, spec_url=origin_url) # Hokey verification but better than nothing: # If all the files with $refs were ingested and validated and an # exception was not thrown, there should be 7 cached file references # in the resolver's store: # 6 json files from tests/data/v2.0/tests_complicated_refs/* # 1 yaml files from tests/data/v2.0/tests_complicated_refs/* assert len([uri for uri in resolver.store.keys() if uri.startswith("file://")]) == 7 def test_specs_with_discriminator(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) validate_spec(swagger_dict) def test_specs_with_discriminator_fail_because_not_required(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) swagger_dict["definitions"]["GenericPet"]["discriminator"] = "name" with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(swagger_dict) assert "discriminator (name) must be a required property" in str(excinfo.value) def test_specs_with_discriminator_fail_because_not_string(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) swagger_dict["definitions"]["GenericPet"]["discriminator"] = "weight" with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(swagger_dict) assert "discriminator (weight) must be a string property" in str(excinfo.value) def test_specs_with_discriminator_fail_because_not_in_properties(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) swagger_dict["definitions"]["GenericPet"]["discriminator"] = "an_other_property" with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(swagger_dict) assert "discriminator (an_other_property) must be defined in properties" in str( excinfo.value ) def test_specs_with_discriminator_in_allOf(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) validate_spec(swagger_dict) def test_specs_with_discriminator_in_allOf_fail_because_not_required(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) swagger_dict["definitions"]["BaseObject"]["discriminator"] = "name" with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(swagger_dict) assert "discriminator (name) must be a required property" in str(excinfo.value) def test_specs_with_discriminator_in_allOf_fail_because_not_string(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) swagger_dict["definitions"]["BaseObject"]["discriminator"] = "weight" with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(swagger_dict) assert "discriminator (weight) must be a string property" in str(excinfo.value) def test_specs_with_discriminator_in_allOf_fail_because_not_in_properties(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) swagger_dict["definitions"]["BaseObject"]["discriminator"] = "an_other_property" with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(swagger_dict) assert "discriminator (an_other_property) must be defined in properties" in str( excinfo.value ) def test_read_yaml_specs(): file_path = "./tests/data/v2.0/test_polymorphic_specs/swagger.json" swagger_dict, _ = get_spec_json_and_url(file_path) swagger_dict["definitions"]["BaseObject"]["discriminator"] = "an_other_property" with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(swagger_dict) assert "discriminator (an_other_property) must be defined in properties" in str( excinfo.value ) @pytest.fixture def default_checks_spec_dict(minimal_swagger_dict): minimal_swagger_dict["definitions"]["bool_string"] = { "properties": { "value": { "type": "string", "enum": ["True", "False"], }, }, } return minimal_swagger_dict @pytest.mark.parametrize( "property_spec", [ {"type": "integer", "default": 1}, {"type": "boolean", "default": True}, {"type": "null", "default": None}, {"type": "number", "default": 2}, {"type": "number", "default": 3.4}, {"type": "object", "default": {"a_random_property": "valid"}}, {"type": "array", "items": {"type": "integer"}, "default": [5, 6, 7]}, {"type": "string", "default": ""}, {"type": "string", "default": None, "x-nullable": True}, {"default": -1}, # if type is not defined any value is a valid value { "type": "array", "items": {"$ref": "#/definitions/bool_string"}, "default": [{"value": "False"}], }, ], ) def test_valid_specs_with_check_of_default_types( default_checks_spec_dict, property_spec ): default_checks_spec_dict["definitions"]["injected_definition"] = { "properties": {"property": property_spec}, } # Success if no exception are raised validate_spec(default_checks_spec_dict) @pytest.mark.parametrize( "property_spec, validator, instance", [ [ {"type": "integer", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "boolean", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "null", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "number", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "object", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "array", "default": "wrong_type"}, "type", "wrong_type", ], [ {"type": "string", "default": -1}, "type", -1, ], [ {"type": "string", "minLength": 100, "default": "short_string"}, "minLength", "short_string", ], [ {"type": ["number", "boolean"], "default": "not_a_number_or_boolean"}, "type", "not_a_number_or_boolean", ], [ { "type": "array", "items": {"$ref": "#/definitions/bool_string"}, "default": [{"value": "not_valid"}], }, "enum", "not_valid", ], ], ) def test_failure_due_to_wrong_default_type( default_checks_spec_dict, property_spec, validator, instance ): default_checks_spec_dict["definitions"]["injected_definition"] = { "properties": {"property": property_spec}, } with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(default_checks_spec_dict) validation_error = excinfo.value.args[1] assert validation_error.instance == instance assert validation_error.validator == validator def test_ref_without_str_argument(minimal_swagger_dict): not_a_ref = {"properties": {"$ref": {"type": "string"}}} minimal_swagger_dict["definitions"]["not_a_ref"] = not_a_ref validate_spec(minimal_swagger_dict) def test_failure_because_references_in_operation_responses(): with open( "./tests/data/v2.0/invalid_swagger_specs_because_ref_in_responses.json" ) as f: invalid_spec = json.load(f) with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(invalid_spec) assert ( "GET /endpoint does not have a valid responses section. " "That section cannot be just a reference to another object." in str(excinfo.value) ) def test_type_array_with_items_succeed_validation(minimal_swagger_dict): minimal_swagger_dict["definitions"] = { "definition_1": { "type": "array", "items": { "type": "string", }, }, } # Success if no exception are raised validate_spec(minimal_swagger_dict) @pytest.mark.parametrize( "swagger_dict_override", ( { "definitions": { "definition_1": { "type": "array", }, }, }, ), ) def test_type_array_without_items_succeed_fails( minimal_swagger_dict, swagger_dict_override ): minimal_swagger_dict.update(swagger_dict_override) with pytest.raises(SwaggerValidationError) as excinfo: validate_spec(minimal_swagger_dict) assert ( str(excinfo.value) == "Definition of type array must define `items` property (definition #/definitions/definition_1)." ) INVALID_SCHEMA_OBJECT = { "type": "object", "properties": {"prop": {"type": "string", "default": 1}}, } @pytest.mark.parametrize( "swagger_dict_override", ( { "definitions": {"definition_1": INVALID_SCHEMA_OBJECT}, }, { "definitions": { "definition_2": { "type": "object", "properties": {"prop": INVALID_SCHEMA_OBJECT}, } }, }, { "parameters": { "param_body": { "in": "body", "name": "body", "schema": INVALID_SCHEMA_OBJECT, }, }, }, { "paths": { "/endpoint": { "get": { "parameters": [ { "in": "body", "name": "body", "schema": INVALID_SCHEMA_OBJECT, } ], "responses": { "200": {"description": "desc"}, }, }, }, }, }, { "paths": { "/endpoint": { "get": { "responses": { "200": { "description": "desc", "schema": INVALID_SCHEMA_OBJECT, }, }, }, }, }, }, ), ) def test_highlight_inconsistent_schema_object_validation( minimal_swagger_dict, swagger_dict_override ): minimal_swagger_dict.update(swagger_dict_override) with pytest.raises(SwaggerValidationError): validate_spec(minimal_swagger_dict) def test_additional_properties_validated( minimal_swagger_dict, default_checks_spec_dict ): invalid_spec = {"type": "object", "required": ["foo"]} minimal_swagger_dict["definitions"]["injected_definition"] = { "type": "object", "additionalProperties": invalid_spec, } with pytest.raises(SwaggerValidationError): validate_spec(minimal_swagger_dict) def test_additional_properties_bool(minimal_swagger_dict, default_checks_spec_dict): minimal_swagger_dict["definitions"]["injected_definition"] = { "type": "object", "additionalProperties": False, } validate_spec(minimal_swagger_dict) def test_array_items_validated(minimal_swagger_dict, default_checks_spec_dict): invalid_spec = {"type": "object", "required": ["foo"]} minimal_swagger_dict["definitions"]["injected_definition"] = { "type": "array", "items": invalid_spec, } with pytest.raises(SwaggerValidationError): validate_spec(minimal_swagger_dict) swagger_spec_validator-3.0.3/tests/validator20/validate_spec_url_test.py000066400000000000000000000036571433033312100266260ustar00rootroot00000000000000import json import os from unittest import mock import pytest from swagger_spec_validator.common import get_uri_from_file_path from swagger_spec_validator.common import SwaggerValidationError from swagger_spec_validator.common import SwaggerValidationWarning from swagger_spec_validator.validator20 import validate_spec_url from tests.conftest import is_urlopen_error def test_success(petstore_contents): with mock.patch( "swagger_spec_validator.validator20.read_url", return_value=json.loads(petstore_contents), ) as mock_read_url: validate_spec_url("http://localhost/api-docs") mock_read_url.assert_called_once_with("http://localhost/api-docs") def test_success_crossref_url_yaml(): urlpath = get_uri_from_file_path(os.path.abspath("./tests/data/v2.0/minimal.yaml")) validate_spec_url(urlpath) def test_success_crossref_url_json(): urlpath = get_uri_from_file_path( os.path.abspath("./tests/data/v2.0/relative_ref.json") ) validate_spec_url(urlpath) def test_complicated_refs_json(): urlpath = get_uri_from_file_path( os.path.abspath("./tests/data/v2.0/test_complicated_refs/swagger.json") ) validate_spec_url(urlpath) def test_specs_with_empty_reference(): with pytest.warns(SwaggerValidationWarning) as warninfo: validate_spec_url( get_uri_from_file_path( os.path.abspath( "./tests/data/v2.0/invalid_swagger_spec_because_empty_reference.yaml" ), ), ) assert ( "Identified $ref with None value. This is usually an error, although technically it might be allowed. " "(path: #/definitions/model1/x-extends)" == str(warninfo.list[0].message) ) def test_raise_SwaggerValidationError_on_urlopen_error(): with pytest.raises(SwaggerValidationError) as excinfo: validate_spec_url("http://foo") assert is_urlopen_error(excinfo.value) swagger_spec_validator-3.0.3/tox.ini000066400000000000000000000014361433033312100175430ustar00rootroot00000000000000[tox] envlist = py37,py38,pre-commit,cover [testenv] deps = -rrequirements-dev.txt commands = py.test {posargs:tests} mypy swagger_spec_validator/ [testenv:pre-commit] deps = pre-commit>1.7.0 setenv = LC_CTYPE=en_US.UTF-8 commands = pre-commit {posargs:run --all-files} [testenv:cover] deps = -rrequirements-dev.txt coverage commands = coverage run --source=swagger_spec_validator/ --omit=swagger_spec_validator/__about__.py -m pytest --capture=no --strict {posargs:tests/} coverage report --omit=.tox/*,tests/*,/usr/share/pyshared/*,/usr/lib/pymodules/* -m [testenv:docs] deps = sphinx sphinx_rtd_theme changedir = docs commands = sphinx-build -b html -d build/doctrees source build/html [flake8] exclude = .tox,virtualenv_*,docs ignore = E501