1from __future__ import absolute_import
2
3from functools import wraps
4
5import falcon
6
7try:
8    import jsonschema
9except ImportError:
10    pass
11
12
13def validate(req_schema=None, resp_schema=None):
14    """Decorator for validating ``req.media`` using JSON Schema.
15
16    This decorator provides standard JSON Schema validation via the
17    ``jsonschema`` package available from PyPI. Semantic validation via
18    the *format* keyword is enabled for the default checkers implemented
19    by ``jsonschema.FormatChecker``.
20
21    Note:
22        The `jsonschema`` package must be installed separately in order to use
23        this decorator, as Falcon does not install it by default.
24
25        See `json-schema.org <http://json-schema.org/>`_ for more
26        information on defining a compatible dictionary.
27
28    Args:
29        req_schema (dict, optional): A dictionary that follows the JSON
30            Schema specification. The request will be validated against this
31            schema.
32        resp_schema (dict, optional): A dictionary that follows the JSON
33            Schema specification. The response will be validated against this
34            schema.
35
36    Example:
37        .. code:: python
38
39            from falcon.media.validators import jsonschema
40
41            # -- snip --
42
43            @jsonschema.validate(my_post_schema)
44            def on_post(self, req, resp):
45
46            # -- snip --
47
48    """
49
50    def decorator(func):
51        @wraps(func)
52        def wrapper(self, req, resp, *args, **kwargs):
53            if req_schema is not None:
54                try:
55                    jsonschema.validate(
56                        req.media, req_schema,
57                        format_checker=jsonschema.FormatChecker()
58                    )
59                except jsonschema.ValidationError as e:
60                    raise falcon.HTTPBadRequest(
61                        'Request data failed validation',
62                        description=e.message
63                    )
64
65            result = func(self, req, resp, *args, **kwargs)
66
67            if resp_schema is not None:
68                try:
69                    jsonschema.validate(
70                        resp.media, resp_schema,
71                        format_checker=jsonschema.FormatChecker()
72                    )
73                except jsonschema.ValidationError:
74                    raise falcon.HTTPInternalServerError(
75                        'Response data failed validation'
76                        # Do not return 'e.message' in the response to
77                        # prevent info about possible internal response
78                        # formatting bugs from leaking out to users.
79                    )
80
81            return result
82        return wrapper
83    return decorator
84