123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- # -*- coding: utf-8 -*-
- """
- This module give access to OpenAPI specifications schemas
- and allows to validate specs against them.
- .. versionadded:: 0.12.1
- """
- from __future__ import unicode_literals
- import io
- import json
- import pkg_resources
- try:
- from collections.abc import Mapping
- except ImportError:
- # TODO Remove this to drop Python2 support
- from collections import Mapping
- from jsonschema import Draft4Validator
- from flask_restx import errors
- class SchemaValidationError(errors.ValidationError):
- """
- Raised when specification is not valid
- .. versionadded:: 0.12.1
- """
- def __init__(self, msg, errors=None):
- super(SchemaValidationError, self).__init__(msg)
- self.errors = errors
- def __str__(self):
- msg = [self.msg]
- for error in sorted(self.errors, key=lambda e: e.path):
- path = ".".join(error.path)
- msg.append("- {}: {}".format(path, error.message))
- for suberror in sorted(error.context, key=lambda e: e.schema_path):
- path = ".".join(suberror.schema_path)
- msg.append(" - {}: {}".format(path, suberror.message))
- return "\n".join(msg)
- __unicode__ = __str__
- class LazySchema(Mapping):
- """
- A thin wrapper around schema file lazy loading the data on first access
- :param filename str: The package relative json schema filename
- :param validator: The jsonschema validator class version
- .. versionadded:: 0.12.1
- """
- def __init__(self, filename, validator=Draft4Validator):
- super(LazySchema, self).__init__()
- self.filename = filename
- self._schema = None
- self._validator = validator
- def _load(self):
- if not self._schema:
- filename = pkg_resources.resource_filename(__name__, self.filename)
- with io.open(filename) as infile:
- self._schema = json.load(infile)
- def __getitem__(self, key):
- self._load()
- return self._schema.__getitem__(key)
- def __iter__(self):
- self._load()
- return self._schema.__iter__()
- def __len__(self):
- self._load()
- return self._schema.__len__()
- @property
- def validator(self):
- """The jsonschema validator to validate against"""
- return self._validator(self)
- #: OpenAPI 2.0 specification schema
- OAS_20 = LazySchema("oas-2.0.json")
- #: Map supported OpenAPI versions to their JSON schema
- VERSIONS = {
- "2.0": OAS_20,
- }
- def validate(data):
- """
- Validate an OpenAPI specification.
- Supported OpenAPI versions: 2.0
- :param data dict: The specification to validate
- :returns boolean: True if the specification is valid
- :raises SchemaValidationError: when the specification is invalid
- :raises flask_restx.errors.SpecsError: when it's not possible to determinate
- the schema to validate against
- .. versionadded:: 0.12.1
- """
- if "swagger" not in data:
- raise errors.SpecsError("Unable to determinate OpenAPI schema version")
- version = data["swagger"]
- if version not in VERSIONS:
- raise errors.SpecsError('Unknown OpenAPI schema version "{}"'.format(version))
- validator = VERSIONS[version].validator
- validation_errors = list(validator.iter_errors(data))
- if validation_errors:
- raise SchemaValidationError(
- "OpenAPI {} validation failed".format(version), errors=validation_errors
- )
- return True
|