# -*- 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
from collections import Mapping
from jsonschema import Draft4Validator
from flask_restplus 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_restplus.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