|
16 | 16 |
|
17 | 17 | import json
|
18 | 18 | from platform import python_version
|
| 19 | +from typing import Callable, Optional |
19 | 20 |
|
20 | 21 | import google.auth
|
21 | 22 | import requests
|
| 23 | +import httpx |
22 | 24 |
|
23 | 25 | import firebase_admin
|
24 | 26 | from firebase_admin import exceptions
|
@@ -128,6 +130,36 @@ def handle_platform_error_from_requests(error, handle_func=None):
|
128 | 130 |
|
129 | 131 | return exc if exc else _handle_func_requests(error, message, error_dict)
|
130 | 132 |
|
| 133 | +def handle_platform_error_from_httpx( |
| 134 | + error: httpx.HTTPError, |
| 135 | + handle_func: Optional[Callable[..., Optional[exceptions.FirebaseError]]] = None |
| 136 | +) -> exceptions.FirebaseError: |
| 137 | + """Constructs a ``FirebaseError`` from the given httpx error. |
| 138 | +
|
| 139 | + This can be used to handle errors returned by Google Cloud Platform (GCP) APIs. |
| 140 | +
|
| 141 | + Args: |
| 142 | + error: An error raised by the httpx module while making an HTTP call to a GCP API. |
| 143 | + handle_func: A function that can be used to handle platform errors in a custom way. When |
| 144 | + specified, this function will be called with three arguments. It has the same |
| 145 | + signature as ```_handle_func_httpx``, but may return ``None``. |
| 146 | +
|
| 147 | + Returns: |
| 148 | + FirebaseError: A ``FirebaseError`` that can be raised to the user code. |
| 149 | + """ |
| 150 | + |
| 151 | + if isinstance(error, httpx.HTTPStatusError): |
| 152 | + response = error.response |
| 153 | + content = response.content.decode() |
| 154 | + status_code = response.status_code |
| 155 | + error_dict, message = _parse_platform_error(content, status_code) |
| 156 | + exc = None |
| 157 | + if handle_func: |
| 158 | + exc = handle_func(error, message, error_dict) |
| 159 | + |
| 160 | + return exc if exc else _handle_func_httpx(error, message, error_dict) |
| 161 | + return handle_httpx_error(error) |
| 162 | + |
131 | 163 |
|
132 | 164 | def handle_operation_error(error):
|
133 | 165 | """Constructs a ``FirebaseError`` from the given operation error.
|
@@ -204,6 +236,60 @@ def handle_requests_error(error, message=None, code=None):
|
204 | 236 | err_type = _error_code_to_exception_type(code)
|
205 | 237 | return err_type(message=message, cause=error, http_response=error.response)
|
206 | 238 |
|
| 239 | +def _handle_func_httpx(error: httpx.HTTPError, message, error_dict) -> exceptions.FirebaseError: |
| 240 | + """Constructs a ``FirebaseError`` from the given GCP error. |
| 241 | +
|
| 242 | + Args: |
| 243 | + error: An error raised by the httpx module while making an HTTP call. |
| 244 | + message: A message to be included in the resulting ``FirebaseError``. |
| 245 | + error_dict: Parsed GCP error response. |
| 246 | +
|
| 247 | + Returns: |
| 248 | + FirebaseError: A ``FirebaseError`` that can be raised to the user code or None. |
| 249 | + """ |
| 250 | + code = error_dict.get('status') |
| 251 | + return handle_httpx_error(error, message, code) |
| 252 | + |
| 253 | + |
| 254 | +def handle_httpx_error(error: httpx.HTTPError, message=None, code=None) -> exceptions.FirebaseError: |
| 255 | + """Constructs a ``FirebaseError`` from the given httpx error. |
| 256 | +
|
| 257 | + This method is agnostic of the remote service that produced the error, whether it is a GCP |
| 258 | + service or otherwise. Therefore, this method does not attempt to parse the error response in |
| 259 | + any way. |
| 260 | +
|
| 261 | + Args: |
| 262 | + error: An error raised by the httpx module while making an HTTP call. |
| 263 | + message: A message to be included in the resulting ``FirebaseError`` (optional). If not |
| 264 | + specified the string representation of the ``error`` argument is used as the message. |
| 265 | + code: A GCP error code that will be used to determine the resulting error type (optional). |
| 266 | + If not specified the HTTP status code on the error response is used to determine a |
| 267 | + suitable error code. |
| 268 | +
|
| 269 | + Returns: |
| 270 | + FirebaseError: A ``FirebaseError`` that can be raised to the user code. |
| 271 | + """ |
| 272 | + if isinstance(error, httpx.TimeoutException): |
| 273 | + return exceptions.DeadlineExceededError( |
| 274 | + message='Timed out while making an API call: {0}'.format(error), |
| 275 | + cause=error) |
| 276 | + if isinstance(error, httpx.ConnectError): |
| 277 | + return exceptions.UnavailableError( |
| 278 | + message='Failed to establish a connection: {0}'.format(error), |
| 279 | + cause=error) |
| 280 | + if isinstance(error, httpx.HTTPStatusError): |
| 281 | + print("printing status error", error) |
| 282 | + if not code: |
| 283 | + code = _http_status_to_error_code(error.response.status_code) |
| 284 | + if not message: |
| 285 | + message = str(error) |
| 286 | + |
| 287 | + err_type = _error_code_to_exception_type(code) |
| 288 | + return err_type(message=message, cause=error, http_response=error.response) |
| 289 | + |
| 290 | + return exceptions.UnknownError( |
| 291 | + message='Unknown error while making a remote service call: {0}'.format(error), |
| 292 | + cause=error) |
207 | 293 |
|
208 | 294 | def _http_status_to_error_code(status):
|
209 | 295 | """Maps an HTTP status to a platform error code."""
|
|
0 commit comments