def __post_init__(self) -> None: convert_params_headers(self.headers) convert_params_headers(self.params) if self.content_type is not None: if self.headers is None: self.headers = dict() MimeType.add_to_headers(self.headers, self.content_type)
def test_from_headers(self, mimetype): headers = {"Content-Type": MimeType.to_string(mimetype)} extracted = MimeType.from_headers(headers) if isinstance(mimetype, MimeType): assert extracted is mimetype else: assert extracted == mimetype
def _validate_response_content( response: requests.Response, data_schema: Optional[Schema] = None) -> Optional[Any]: if data_schema is None: return None try: try: mimetype: Optional[MimeTypeTolerant] = MimeType.from_name( response.headers.get("Content-Type")) except ValueError: mimetype = None loaded, _ = decode_content( response.content, mimetype=mimetype, data_schema=data_schema, allow_sniff=True, ) return loaded except (json.JSONDecodeError, InvalidBSON, InvalidDocument, UnicodeDecodeError): raise ContentDecodeError("Could not load response data.") except ValidationError: raise DataValidationError("Error validating returned data")
def test_schema_round_trip(self, mimetype): data = DataToTest() schema = SchemaToTest() headers = dict() encoded = encode_content( data, mimetype=mimetype, headers=headers, data_schema=schema ) print(encoded.decode()) assert isinstance(encoded, bytes) header_content_type = headers["Content-Type"] if mimetype is None: assert header_content_type == MimeType.JSON.value else: assert header_content_type == MimeType.from_name(mimetype).value loaded, decoded = decode_content( encoded, mimetype=mimetype, data_schema=schema, allow_sniff=True ) assert isinstance(loaded, DataToTest) assert loaded == data assert isinstance(decoded, Mapping)
def mimetype(self) -> Union[str, MimeType]: """Mimetype pulled from ``'Content-Type'`` request header.""" mimetype = super().mimetype try: return MimeType.from_name(mimetype) except ValueError: return mimetype
async def execute(self) -> ResponseData: """ Executes request and handles response from spanreed endpoint. """ params = copy.copy(self.endpoint_settings.query_params) params.update(self.query_params) for key, value in self.projection.items(): params["project." + key] = str(value) convert_params_headers(params) if self._paging is not None: self._paging.offset += self._paging.offset_start params["paging-offset"] = str(self._paging.offset) params["paging-limit"] = str(self._paging.limit) headers = copy.copy(self.endpoint_settings.headers) headers.update(self.headers) convert_params_headers(self.headers) if self.mimetype_accept is not None: headers["Accept"] = MimeType.to_string(self.mimetype_accept) base_url = (f"{self.client.protocol}://{self.client.host_name}" f"{self.endpoint_settings.endpoint}") url = base_url.format(**self.path_params) req_schema = self.endpoint_settings.req_schema try: data = encode_content( content=self.media, mimetype=self.mimetype_send, headers=headers, data_schema=req_schema, encoders=self.client._ENCODERS, ) except ContentTypeUnknownBase as error: raise ContentTypeUnknownError(str(error), response=None) # allow for method to be passed in caps. method = self.endpoint_settings.method.lower() method_func = getattr(self.client.session, method) response = await method_func(url=url, params=params, headers=headers, data=data) self.executed = True return await handle_response_aio( response=response, valid_status_codes=self.endpoint_settings.resp_codes, data_schema=self.endpoint_settings.resp_schema, api_errors_additional=self.client.api_error_index, current_data_object=self.update_obj, data_object_updater=self.endpoint_settings.data_updater, decoders=self.client._DECODERS, )
def register_mimetype(mimetype: MimeTypeTolerant, encoder: EncoderType, decoder: DecoderType) -> None: try: mimetype = MimeType.from_name(mimetype) except ValueError: pass SpanClient._ENCODERS[mimetype] = encoder SpanClient._DECODERS[mimetype] = decoder
def __post_init__(self) -> None: self.content_type = None mock_text = self._text if self._json is not None: self.mock_json(self._json) elif self._yaml is not None: self.mock_yaml(self._yaml) elif self._bson is not None: self.mock_bson(self._bson) elif mock_text is not None: self.mock_text(mock_text) if self._content_type is not None: self.content_type = MimeType.to_string( self._content_type) # type: ignore if self.content_type is not None: MimeType.add_to_headers(self.headers, self.content_type) if self._exception is not None: self.mock_exception(self._exception)
def test_data_round_trip(self, mimetype): data = {"key": 10} headers = dict() encoded = encode_content(data, mimetype=mimetype, headers=headers) print(str(encoded)) assert isinstance(encoded, bytes) assert headers["Content-Type"] == MimeType.from_name(mimetype).value loaded, decoded = decode_content(encoded, mimetype=mimetype) assert dict(decoded) == dict(loaded) == data
def register_mimetype(self, mimetype: MimeTypeTolerant, encoder: EncoderType, decoder: DecoderType) -> None: """ Registers encoder and decoder function for a given mimetype. :param mimetype: to register for ex: ``'text/csv'``. :param encoder: Encodes mimetype data to binary. :param decoder: Decodes mimetype data to binary. :return: """ try: mimetype = MimeType.from_name(mimetype) except ValueError: pass self._encoders[mimetype] = encoder self._decoders[mimetype] = decoder
def mimetype(self) -> MimeTypeTolerant: if self._mimetype is NOT_LOADED: # Check the builtin content type first. mimetype: MimeTypeTolerant = self.message.content_type if not mimetype: # if that is none, check the custom header. mimetype = self.headers.get("Content-Type") if mimetype: try: mimetype = MimeType.from_name(mimetype) except ValueError: pass else: mimetype = None self._mimetype = mimetype return self._mimetype
def validate_request( self, req_url: str, req_headers: MutableMapping[str, str], req_params: MutableMapping[str, str], req_data: Optional[bytes], mock_response: MockResponse, ) -> None: self.req_url = req_url self.req_params = req_params self.req_headers = req_headers self.req_data = req_data self.req_data_decoded = None self.validate_url() self.validate_headers() self.validate_params() mimetype = MimeType.from_headers(req_headers) self.validate_media(mimetype=mimetype) self.validate_media_type(mimetype) if self.custom_hook is not None: self.custom_hook(self, mock_response)
def test_to_string_none_error(self): with pytest.raises(ValueError): MimeType.to_string(None)
def test_is_mimetype_none(self): assert MimeType.is_mimetype(None, MimeType.JSON) is False
def test_mimetype_parsing(self, name: str): assert MimeType.from_name(name) is MimeType.JSON
async def handle_response_aio( response: ClientResponse, valid_status_codes: Union[int, Tuple[int, ...]] = 200, data_schema: Optional[Union[Schema, MimeType]] = None, api_errors_additional: Optional[Dict[int, Type[APIError]]] = None, current_data_object: Optional[ModelType] = None, data_object_updater: Optional[Callable[[ModelType, Any], None]] = None, decoders: DecoderIndexType = DEFAULT_DECODERS, ) -> ResponseData: """ Examines response from SpanReed service and raises reported errors. :param response: from aiohttp :param valid_status_codes: Valid return http code(s). :param data_schema: Schema object for loading responses. :param api_errors_additional: Code, Error Class Mapping of Additional APIError types the response may return. :param current_data_object: Current object which represents response payload. Will be updated in-place with response data. :param data_object_updater: Callable which takes args: (current_data_object, new_data_object). Used to update current_data_object in place of the default updater. :return: Loaded data, raw data mapping (dict or bson record). :raises ResponseStatusError: If status code does match. :raises ContentTypeUnknownError: If content-type is not a type that is known. :raises marshmallow.ValidationError: If data not consistent with schema. """ # Try to raise error, catch if it does not exist. try: raise Error.from_headers(response.headers).to_exception(api_errors_additional) except NoErrorReturnedError: pass _check_status_code( received_code=response.status, valid_status_codes=valid_status_codes, response=response, ) content = await response.read() if content or data_schema is not None: try: loaded_data, decoded_data = decode_content( content=content, mimetype=MimeType.from_headers(response.headers), data_schema=data_schema, allow_sniff=True, decoders=decoders, ) except ContentDecodeBase as error: raise ContentDecodeError(str(error), response=response) except ContentTypeUnknownBase as error: raise ContentTypeUnknownError(str(error), response=response) else: loaded_data, decoded_data = None, None if current_data_object is not None: _update_data( current_data_object=current_data_object, new_data_object=loaded_data, object_updater=data_object_updater, ) loaded_data = current_data_object return ResponseData(resp=response, loaded=loaded_data, decoded=decoded_data)
def test_add_none_to_headers(self): headers = dict() MimeType.add_to_headers(headers, None) assert headers == dict()