mirror of https://github.com/morpheus65535/bazarr
126 lines
3.4 KiB
Python
126 lines
3.4 KiB
Python
|
# -*- 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
|