Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/connexion/validators/json.py: 40%
73 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:12 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:12 +0000
1import json
2import logging
3import typing as t
5import jsonschema
6from jsonschema import Draft4Validator, ValidationError, draft4_format_checker
7from starlette.types import Scope
9from connexion.exceptions import BadRequestProblem, NonConformingResponseBody
10from connexion.json_schema import (
11 Draft4RequestValidator,
12 Draft4ResponseValidator,
13 format_error_with_path,
14)
15from connexion.validators import (
16 AbstractRequestBodyValidator,
17 AbstractResponseBodyValidator,
18)
20logger = logging.getLogger(__name__)
23class JSONRequestBodyValidator(AbstractRequestBodyValidator):
24 """Request body validator for json content types."""
26 def __init__(
27 self,
28 *,
29 schema: dict,
30 required=False,
31 nullable=False,
32 encoding: str,
33 strict_validation: bool,
34 **kwargs,
35 ) -> None:
36 super().__init__(
37 schema=schema,
38 required=required,
39 nullable=nullable,
40 encoding=encoding,
41 strict_validation=strict_validation,
42 )
44 @property
45 def _validator(self):
46 return Draft4RequestValidator(
47 self._schema, format_checker=draft4_format_checker
48 )
50 async def _parse(
51 self, stream: t.AsyncGenerator[bytes, None], scope: Scope
52 ) -> t.Any:
53 bytes_body = b"".join([message async for message in stream])
54 body = bytes_body.decode(self._encoding)
56 if not body:
57 return None
59 try:
60 return json.loads(body)
61 except json.decoder.JSONDecodeError as e:
62 raise BadRequestProblem(detail=str(e))
64 def _validate(self, body: dict) -> None:
65 try:
66 return self._validator.validate(body)
67 except ValidationError as exception:
68 error_path_msg = format_error_with_path(exception=exception)
69 logger.error(
70 f"Validation error: {exception.message}{error_path_msg}",
71 extra={"validator": "body"},
72 )
73 raise BadRequestProblem(detail=f"{exception.message}{error_path_msg}")
76class DefaultsJSONRequestBodyValidator(JSONRequestBodyValidator):
77 """Request body validator for json content types which fills in default values. This Validator
78 intercepts the body, makes changes to it, and replays it for the next ASGI application."""
80 MUTABLE_VALIDATION = True
81 """This validator might mutate to the body."""
83 @property
84 def _validator(self):
85 validator_cls = self.extend_with_set_default(Draft4RequestValidator)
86 return validator_cls(self._schema, format_checker=draft4_format_checker)
88 # via https://python-jsonschema.readthedocs.io/
89 @staticmethod
90 def extend_with_set_default(validator_class):
91 validate_properties = validator_class.VALIDATORS["properties"]
93 def set_defaults(validator, properties, instance, schema):
94 for property, subschema in properties.items():
95 if "default" in subschema:
96 instance.setdefault(property, subschema["default"])
98 yield from validate_properties(validator, properties, instance, schema)
100 return jsonschema.validators.extend(
101 validator_class, {"properties": set_defaults}
102 )
105class JSONResponseBodyValidator(AbstractResponseBodyValidator):
106 """Response body validator for json content types."""
108 @property
109 def validator(self) -> Draft4Validator:
110 return Draft4ResponseValidator(
111 self._schema, format_checker=draft4_format_checker
112 )
114 def _parse(self, stream: t.Generator[bytes, None, None]) -> t.Any:
115 body = b"".join(stream).decode(self._encoding)
117 if not body:
118 return None
120 try:
121 return json.loads(body)
122 except json.decoder.JSONDecodeError as e:
123 raise NonConformingResponseBody(str(e))
125 def _validate(self, body: dict):
126 try:
127 self.validator.validate(body)
128 except ValidationError as exception:
129 error_path_msg = format_error_with_path(exception=exception)
130 logger.error(
131 f"Validation error: {exception.message}{error_path_msg}",
132 extra={"validator": "body"},
133 )
134 raise NonConformingResponseBody(
135 detail=f"Response body does not conform to specification. {exception.message}{error_path_msg}"
136 )
139class TextResponseBodyValidator(JSONResponseBodyValidator):
140 def _parse(self, stream: t.Generator[bytes, None, None]) -> str: # type: ignore
141 body = b"".join(stream).decode(self._encoding)
143 try:
144 return json.loads(body)
145 except json.decoder.JSONDecodeError:
146 return body