def make_put_request(self, url, payload=None, params=None): """ Make a PUT request using the provided ``url`` with required payload. The ``payload`` must be a dict that can be converted into a JSON object (via ``json.dumps``). :return: The decoded response. """ if params is not None and params.get('key', False) is False: params['key'] = self.key else: params = self.default_params payload = json.dumps(payload) headers = self.json_headers r = requests.put(url, data=payload, params=params, headers=headers, verify=self.verify) if r.status_code == 200: try: return r.json() except Exception as e: raise ConnectionError( "Request was successful, but cannot decode the response content: %s" % e, body=r.content, status_code=r.status_code) # @see self.body for HTTP response body raise ConnectionError("Unexpected HTTP status code: %s" % r.status_code, body=r.text, status_code=r.status_code)
def make_patch_request(self, url, payload=None, params=None): """ Make a PATCH request using the provided ``url`` with required payload. :type payload: dict :param payload: a JSON-serializable dictionary :return: The decoded response. """ payload = json.dumps(payload) headers = self.json_headers r = requests.patch(url, data=payload, params=params, headers=headers, verify=self.verify, timeout=self.timeout, allow_redirects=False) if r.status_code == 200: try: return r.json() except Exception as e: raise ConnectionError( "Request was successful, but cannot decode the response content: %s" % e, body=r.content, status_code=r.status_code) # @see self.body for HTTP response body raise ConnectionError("Unexpected HTTP status code: %s" % r.status_code, body=r.text, status_code=r.status_code)
def make_post_request(self, url, payload, params=None, files_attached=False): """ Make a POST request using the provided ``url`` and ``payload``. The ``payload`` must be a dict that contains the request values. The payload dict may contain file handles (in which case the files_attached flag must be set to true). If the ``params`` are not provided, use ``default_params`` class field. If params are provided and the provided dict does not have ``key`` key, the default ``self.key`` value will be included in what's passed to the server via the request. :return: The decoded response. """ if params is not None and params.get('key', False) is False: params['key'] = self.key else: params = self.default_params # Compute data, headers, params arguments for request.post, # leveraging the requests-toolbelt library if any files have # been attached. if files_attached: payload.update(params) payload = MultipartEncoder(fields=payload) headers = self.json_headers.copy() headers['Content-Type'] = payload.content_type post_params = {} else: payload = json.dumps(payload) headers = self.json_headers post_params = params r = requests.post(url, data=payload, headers=headers, verify=self.verify, params=post_params) if r.status_code == 200: try: return r.json() except Exception as e: raise ConnectionError( "Request was successful, but cannot decode the response content: %s" % e, body=r.content, status_code=r.status_code) # @see self.body for HTTP response body raise ConnectionError("Unexpected HTTP status code: %s" % r.status_code, body=r.text, status_code=r.status_code)
def _get(self, id=None, deleted=False, contents=None, url=None, params=None, json=True): """ Do a GET request, composing the URL from ``id``, ``deleted`` and ``contents``. Alternatively, an explicit ``url`` can be provided. If ``json`` is set to ``True``, return a decoded JSON object (and treat an empty or undecodable response as an error). The request will optionally be retried as configured by ``max_get_retries`` and ``get_retry_delay``: this offers some resilience in the presence of temporary failures. :return: The decoded response if ``json`` is set to ``True``, otherwise the response object """ if not url: url = self.gi._make_url(self, module_id=id, deleted=deleted, contents=contents) attempts_left = self.max_get_retries() retry_delay = self.get_retry_delay() bioblend.log.debug("GET - attempts left: %s; retry delay: %s", attempts_left, retry_delay) msg = '' while attempts_left > 0: attempts_left -= 1 try: r = self.gi.make_get_request(url, params=params) except (requests.exceptions.ConnectionError, ProtocolError) as e: msg = str(e) r = requests.Response( ) # empty Response object used when raising ConnectionError else: if r.status_code == 200: if not json: return r elif not r.content: msg = "GET: empty response" else: try: return r.json() except ValueError: msg = "GET: invalid JSON : %r" % (r.content, ) else: msg = "GET: error %s: %r" % (r.status_code, r.content) msg = "%s, %d attempts left" % (msg, attempts_left) if attempts_left <= 0: bioblend.log.error(msg) raise ConnectionError(msg, body=r.text, status_code=r.status_code) else: bioblend.log.warning(msg) time.sleep(retry_delay)
def _delete(self, payload=None, id=None, deleted=False, contents=None, url=None, params=None): """ Do a generic DELETE request, composing the url from the contents of the arguments. Alternatively, an explicit ``url`` can be provided to use for the request. ``payload`` must be a dict that contains additional request arguments which will be sent along with the request body. :return: The decoded response. """ if not url: url = self.gi._make_url(self, module_id=id, deleted=deleted, contents=contents) r = self.gi.make_delete_request(url, payload=payload, params=params) if r.status_code == 200: return r.json() # @see self.body for HTTP response body raise ConnectionError("Unexpected HTTP status code: %s" % r.status_code, body=r.text, status_code=r.status_code)
def _delete(self, payload=None, id=None, deleted=False, contents=None, url=None, params=None): """ Do a generic DELETE request, composing the url from the contents of the arguments. Alternatively, an explicit ``url`` can be provided to use for the request. :type payload: dict :param payload: additional parameters to send in the body of the request :return: The decoded response. """ if not url: url = self._make_url(module_id=id, deleted=deleted, contents=contents) r = self.gi.make_delete_request(url, payload=payload, params=params) if r.status_code == 200: return r.json() # @see self.body for HTTP response body raise ConnectionError( f"Unexpected HTTP status code: {r.status_code}", body=r.text, status_code=r.status_code, )
def make_post_request(self, url, payload=None, params=None, files_attached=False): """ Make a POST request using the provided ``url`` and ``payload``. The ``payload`` must be a dict that contains the request values. The payload dict may contain file handles (in which case the files_attached flag must be set to true). If the ``params`` are not provided, use ``default_params`` class field. If params are provided and the provided dict does not have ``key`` key, the default ``self.key`` value will be included in what's passed to the server via the request. :return: The decoded response. """ def my_dumps(d): """ Apply ``json.dumps()`` to the values of the dict ``d`` if they are not of type ``FileStream``. """ for k, v in d.items(): if not isinstance(v, FileStream): d[k] = json.dumps(v) return d # Compute data, headers, params arguments for request.post, # leveraging the requests-toolbelt library if any files have # been attached. if files_attached: payload = my_dumps(payload) if params: payload.update(params) payload = MultipartEncoder(fields=payload) headers = self.json_headers.copy() headers['Content-Type'] = payload.content_type post_params = None else: if payload is not None: payload = json.dumps(payload) headers = self.json_headers post_params = params r = requests.post( url, params=post_params, data=payload, headers=headers, timeout=self.timeout, allow_redirects=False, verify=self.verify, ) if r.status_code == 200: try: return r.json() except Exception as e: raise ConnectionError( f"Request was successful, but cannot decode the response content: {e}", body=r.content, status_code=r.status_code, ) # @see self.body for HTTP response body raise ConnectionError( f"Unexpected HTTP status code: {r.status_code}", body=r.text, status_code=r.status_code, )