Skip to content

Commit

Permalink
feat(validator): add support for jsonschema_rs validator library
Browse files Browse the repository at this point in the history
  • Loading branch information
CaselIT committed Apr 4, 2024
1 parent 5cecd69 commit d8f2bfa
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
1 change: 1 addition & 0 deletions falcon/media/validators/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import jsonschema
from . import jsonschema_rs
125 changes: 125 additions & 0 deletions falcon/media/validators/jsonschema_rs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from __future__ import annotations

from typing import Any

from . import base as _base

try:
import jsonschema_rs
except ImportError: # pragma: nocover
pass


def validate(req_schema: Any = None, resp_schema: Any = None, is_async: bool = False):
"""Validate ``req.media`` using JSON Schema.
This decorator provides standard JSON Schema validation via the
``jsonschema_rs`` package available from PyPI.
In the case of failed request media validation, an instance of
:class:`~falcon.MediaValidationError` is raised by the decorator. By
default, this error is rendered as a 400 (:class:`~falcon.HTTPBadRequest`)
response with the ``title`` and ``description`` attributes explaining the
validation failure, but this behavior can be modified by adding a
custom error :func:`handler <falcon.App.add_error_handler>` for
:class:`~falcon.MediaValidationError`.
Note:
The ``jsonschema_rs`` package must be installed separately in order to use
this decorator, as Falcon does not install it by default.
See `jsonschema_rs PyPi <https://pypi.org/project/jsonschema-rs/>`_ for more
information on defining a compatible dictionary.
Keyword Args:
req_schema (dict or str): A dictionary that follows the JSON
Schema specification. The request will be validated against this
schema.
Can be also a json string that will be loaded by the jsonschema_rs library
resp_schema (dict or str): A dictionary that follows the JSON
Schema specification. The response will be validated against this
schema.
Can be also a json string that will be loaded by the jsonschema_rs library
is_async (bool): Set to ``True`` for ASGI apps to provide a hint that
the decorated responder is a coroutine function (i.e., that it
is defined with ``async def``) or that it returns an awaitable
coroutine object.
Normally, when the function source is declared using ``async def``,
the resulting function object is flagged to indicate it returns a
coroutine when invoked, and this can be automatically detected.
However, it is possible to use a regular function to return an
awaitable coroutine object, in which case a hint is required to let
the framework know what to expect. Also, a hint is always required
when using a cythonized coroutine function, since Cython does not
flag them in a way that can be detected in advance, even when the
function is declared using ``async def``.
Example:
.. tabs::
.. tab:: WSGI
.. code:: python
from falcon.media.validators import jsonschema_rs
# -- snip --
@jsonschema_rs.validate(my_post_schema)
def on_post(self, req, resp):
# -- snip --
.. tab:: ASGI
.. code:: python
from falcon.media.validators import jsonschema_rs
# -- snip --
@jsonschema_rs.validate(my_post_schema)
async def on_post(self, req, resp):
# -- snip --
.. tab:: ASGI (Cythonized App)
.. code:: python
from falcon.media.validators import jsonschema_rs
# -- snip --
@jsonschema_rs.validate(my_post_schema, is_async=True)
async def on_post(self, req, resp):
# -- snip --
"""

return _base.validator_factory(

Check warning on line 103 in falcon/media/validators/jsonschema_rs.py

View check run for this annotation

Codecov / codecov/patch

falcon/media/validators/jsonschema_rs.py#L103

Added line #L103 was not covered by tests
JsonSchemaRsValidator, req_schema, resp_schema, is_async
)


class JsonSchemaRsValidator(_base.Validator):
def __init__(self, schema: Any) -> None:
self.schema = schema

Check warning on line 110 in falcon/media/validators/jsonschema_rs.py

View check run for this annotation

Codecov / codecov/patch

falcon/media/validators/jsonschema_rs.py#L110

Added line #L110 was not covered by tests
if isinstance(schema, str):
self.validator = jsonschema_rs.JSONSchema.from_str(schema)

Check warning on line 112 in falcon/media/validators/jsonschema_rs.py

View check run for this annotation

Codecov / codecov/patch

falcon/media/validators/jsonschema_rs.py#L112

Added line #L112 was not covered by tests
else:
self.validator = jsonschema_rs.JSONSchema(schema)
self.exceptions = jsonschema_rs.ValidationError

Check warning on line 115 in falcon/media/validators/jsonschema_rs.py

View check run for this annotation

Codecov / codecov/patch

falcon/media/validators/jsonschema_rs.py#L114-L115

Added lines #L114 - L115 were not covered by tests

@classmethod
def from_schema(cls, schema: Any) -> JsonSchemaRsValidator:
return cls(schema)

Check warning on line 119 in falcon/media/validators/jsonschema_rs.py

View check run for this annotation

Codecov / codecov/patch

falcon/media/validators/jsonschema_rs.py#L119

Added line #L119 was not covered by tests

def validate(self, media: Any) -> None:
self.validator.validate(media)

Check warning on line 122 in falcon/media/validators/jsonschema_rs.py

View check run for this annotation

Codecov / codecov/patch

falcon/media/validators/jsonschema_rs.py#L122

Added line #L122 was not covered by tests

def get_exception_message(self, exception: jsonschema_rs.ValidationError):
return exception.message

Check warning on line 125 in falcon/media/validators/jsonschema_rs.py

View check run for this annotation

Codecov / codecov/patch

falcon/media/validators/jsonschema_rs.py#L125

Added line #L125 was not covered by tests

0 comments on commit d8f2bfa

Please sign in to comment.