aboutsummaryrefslogtreecommitdiffhomepage
path: root/libs/flask_restx/schemas/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/flask_restx/schemas/__init__.py')
-rw-r--r--libs/flask_restx/schemas/__init__.py125
1 files changed, 125 insertions, 0 deletions
diff --git a/libs/flask_restx/schemas/__init__.py b/libs/flask_restx/schemas/__init__.py
new file mode 100644
index 000000000..d6dc2ac0f
--- /dev/null
+++ b/libs/flask_restx/schemas/__init__.py
@@ -0,0 +1,125 @@
+# -*- 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