celitech-sdk 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celitech/__init__.py +2 -0
- celitech/hooks/__init__.py +0 -0
- celitech/hooks/hook.py +95 -0
- celitech/models/__init__.py +14 -0
- celitech/models/base.py +251 -0
- celitech/models/create_purchase_ok_response.py +85 -0
- celitech/models/create_purchase_request.py +54 -0
- celitech/models/edit_purchase_ok_response.py +41 -0
- celitech/models/edit_purchase_request.py +41 -0
- celitech/models/get_esim_device_ok_response.py +41 -0
- celitech/models/get_esim_history_ok_response.py +50 -0
- celitech/models/get_esim_mac_ok_response.py +39 -0
- celitech/models/get_esim_ok_response.py +43 -0
- celitech/models/get_purchase_consumption_ok_response.py +17 -0
- celitech/models/list_destinations_ok_response.py +38 -0
- celitech/models/list_packages_ok_response.py +61 -0
- celitech/models/list_purchases_ok_response.py +129 -0
- celitech/models/top_up_esim_ok_response.py +82 -0
- celitech/models/top_up_esim_request.py +49 -0
- celitech/models/utils/cast_models.py +81 -0
- celitech/models/utils/json_map.py +80 -0
- celitech/net/__init__.py +0 -0
- celitech/net/environment/__init__.py +1 -0
- celitech/net/environment/environment.py +11 -0
- celitech/net/headers/__init__.py +0 -0
- celitech/net/headers/base_header.py +9 -0
- celitech/net/request_chain/__init__.py +0 -0
- celitech/net/request_chain/handlers/__init__.py +0 -0
- celitech/net/request_chain/handlers/base_handler.py +39 -0
- celitech/net/request_chain/handlers/hook_handler.py +47 -0
- celitech/net/request_chain/handlers/http_handler.py +80 -0
- celitech/net/request_chain/handlers/retry_handler.py +65 -0
- celitech/net/request_chain/request_chain.py +57 -0
- celitech/net/transport/__init__.py +0 -0
- celitech/net/transport/request.py +89 -0
- celitech/net/transport/request_error.py +53 -0
- celitech/net/transport/response.py +57 -0
- celitech/net/transport/serializer.py +249 -0
- celitech/net/transport/utils.py +28 -0
- celitech/sdk.py +30 -0
- celitech/services/__init__.py +0 -0
- celitech/services/destinations.py +29 -0
- celitech/services/e_sim.py +121 -0
- celitech/services/packages.py +72 -0
- celitech/services/purchases.py +189 -0
- celitech/services/utils/base_service.py +63 -0
- celitech/services/utils/default_headers.py +57 -0
- celitech/services/utils/validator.py +170 -0
- celitech_sdk-1.0.0.dist-info/LICENSE +21 -0
- celitech_sdk-1.0.0.dist-info/METADATA +482 -0
- celitech_sdk-1.0.0.dist-info/RECORD +53 -0
- celitech_sdk-1.0.0.dist-info/WHEEL +5 -0
- celitech_sdk-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import Optional, Tuple
|
|
2
|
+
from ...transport.request import Request
|
|
3
|
+
from ...transport.response import Response
|
|
4
|
+
from ...transport.request_error import RequestError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseHandler:
|
|
8
|
+
"""
|
|
9
|
+
A class for sending the request through the chain of handlers.
|
|
10
|
+
|
|
11
|
+
:ivar BaseHandler _next_handler: The next handler in the chain.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
"""
|
|
16
|
+
Initialize a new instance of BaseHandler.
|
|
17
|
+
"""
|
|
18
|
+
self._next_handler = None
|
|
19
|
+
|
|
20
|
+
def handle(
|
|
21
|
+
self, request: Request
|
|
22
|
+
) -> Tuple[Optional[Response], Optional[RequestError]]:
|
|
23
|
+
"""
|
|
24
|
+
Process the given request and return a response or an error.
|
|
25
|
+
This method must be implemented by all subclasses.
|
|
26
|
+
|
|
27
|
+
:param Request request: The request to handle.
|
|
28
|
+
:return: The response and any error that occurred.
|
|
29
|
+
:rtype: Tuple[Optional[Response], Optional[RequestError]]
|
|
30
|
+
"""
|
|
31
|
+
raise NotImplementedError()
|
|
32
|
+
|
|
33
|
+
def set_next(self, handler: "BaseHandler"):
|
|
34
|
+
"""
|
|
35
|
+
Set the next handler in the chain.
|
|
36
|
+
|
|
37
|
+
:param BaseHandler handler: The next handler.
|
|
38
|
+
"""
|
|
39
|
+
self._next_handler = handler
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from typing import Optional, Tuple
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from .base_handler import BaseHandler
|
|
5
|
+
from ....hooks.hook import CustomHook
|
|
6
|
+
from ...transport.request import Request
|
|
7
|
+
from ...transport.response import Response
|
|
8
|
+
from ...transport.request_error import RequestError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HookHandler(BaseHandler):
|
|
12
|
+
"""
|
|
13
|
+
Handler for calling hooks.
|
|
14
|
+
|
|
15
|
+
:ivar Hook _hook: The hook to be called. This is a placeholder and should be replaced with an instance of the actual hook class.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
"""
|
|
20
|
+
Initialize a new instance of HookHandler.
|
|
21
|
+
"""
|
|
22
|
+
super().__init__()
|
|
23
|
+
self._hook = CustomHook()
|
|
24
|
+
|
|
25
|
+
def handle(
|
|
26
|
+
self, request: Request
|
|
27
|
+
) -> Tuple[Optional[Response], Optional[RequestError]]:
|
|
28
|
+
"""
|
|
29
|
+
Call the beforeRequest hook before passing the request to the next handler in the chain.
|
|
30
|
+
Call the afterResponse hook after receiving a response from the next handler in the chain.
|
|
31
|
+
Call the onError hook if an error occurs in the next handler in the chain.
|
|
32
|
+
|
|
33
|
+
:param Request request: The request to handle.
|
|
34
|
+
:return: The response and any error that occurred.
|
|
35
|
+
:rtype: Tuple[Optional[Response], Optional[RequestError]]
|
|
36
|
+
"""
|
|
37
|
+
if self._next_handler is None:
|
|
38
|
+
raise RequestError("Handler chain is incomplete")
|
|
39
|
+
|
|
40
|
+
self._hook.before_request(request)
|
|
41
|
+
response, error = self._next_handler.handle(request)
|
|
42
|
+
if error is not None and error.is_http_error:
|
|
43
|
+
self._hook.on_error(error, request, error.response)
|
|
44
|
+
else:
|
|
45
|
+
self._hook.after_response(request, response)
|
|
46
|
+
|
|
47
|
+
return response, error
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from requests.exceptions import Timeout
|
|
4
|
+
from typing import Optional, Tuple
|
|
5
|
+
from .base_handler import BaseHandler
|
|
6
|
+
from ...transport.request import Request
|
|
7
|
+
from ...transport.response import Response
|
|
8
|
+
from ...transport.request_error import RequestError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HttpHandler(BaseHandler):
|
|
12
|
+
"""
|
|
13
|
+
Handler for making HTTP requests.
|
|
14
|
+
This handler sends the request to the specified URL and returns the response.
|
|
15
|
+
|
|
16
|
+
:ivar int _timeout_in_seconds: The timeout for the HTTP request in seconds.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
"""
|
|
21
|
+
Initialize a new instance of HttpHandler.
|
|
22
|
+
"""
|
|
23
|
+
super().__init__()
|
|
24
|
+
self._timeout_in_seconds = 60
|
|
25
|
+
|
|
26
|
+
def handle(
|
|
27
|
+
self, request: Request
|
|
28
|
+
) -> Tuple[Optional[Response], Optional[RequestError]]:
|
|
29
|
+
"""
|
|
30
|
+
Send the request to the specified URL and return the response.
|
|
31
|
+
|
|
32
|
+
:param Request request: The request to send.
|
|
33
|
+
:return: The response and any error that occurred.
|
|
34
|
+
:rtype: Tuple[Optional[Response], Optional[RequestError]]
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
request_args = self._get_request_data(request)
|
|
38
|
+
|
|
39
|
+
result = requests.request(
|
|
40
|
+
request.method,
|
|
41
|
+
request.url,
|
|
42
|
+
headers=request.headers,
|
|
43
|
+
timeout=self._timeout_in_seconds,
|
|
44
|
+
**request_args,
|
|
45
|
+
)
|
|
46
|
+
response = Response(result)
|
|
47
|
+
|
|
48
|
+
if result.status_code >= 400:
|
|
49
|
+
return None, RequestError(
|
|
50
|
+
message=f"{result.status_code} error in request to: {request.url}",
|
|
51
|
+
status_code=result.status_code,
|
|
52
|
+
response=response,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return response, None
|
|
56
|
+
except Timeout:
|
|
57
|
+
return None, RequestError("Request timed out")
|
|
58
|
+
|
|
59
|
+
def _get_request_data(self, request: Request) -> dict:
|
|
60
|
+
"""
|
|
61
|
+
Get the request arguments based on the request headers and data.
|
|
62
|
+
|
|
63
|
+
:param Request request: The request object.
|
|
64
|
+
:return: The request arguments.
|
|
65
|
+
:rtype: dict
|
|
66
|
+
"""
|
|
67
|
+
headers = request.headers or {}
|
|
68
|
+
data = request.body or {}
|
|
69
|
+
content_type = headers.get("Content-Type", "application/json")
|
|
70
|
+
|
|
71
|
+
if request.method == "GET" and not data:
|
|
72
|
+
return {}
|
|
73
|
+
|
|
74
|
+
if content_type.startswith("application/") and "json" in content_type:
|
|
75
|
+
return {"json": data}
|
|
76
|
+
|
|
77
|
+
if "multipart/form-data" in content_type:
|
|
78
|
+
return {"files": data}
|
|
79
|
+
|
|
80
|
+
return {"data": data}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
from time import sleep
|
|
5
|
+
from .base_handler import BaseHandler
|
|
6
|
+
from ...transport.request import Request
|
|
7
|
+
from ...transport.response import Response
|
|
8
|
+
from ...transport.request_error import RequestError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RetryHandler(BaseHandler):
|
|
12
|
+
"""
|
|
13
|
+
Handler for retrying requests.
|
|
14
|
+
Retries the request if the previous handler in the chain returned an error or a response with a status code of 500 or higher.
|
|
15
|
+
|
|
16
|
+
:ivar int _max_attempts: The maximum number of retry attempts.
|
|
17
|
+
:ivar int _delay_in_milliseconds: The delay between retry attempts in milliseconds.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
"""
|
|
22
|
+
Initialize a new instance of RetryHandler.
|
|
23
|
+
"""
|
|
24
|
+
super().__init__()
|
|
25
|
+
self._max_attempts = 3
|
|
26
|
+
self._delay_in_milliseconds = 150
|
|
27
|
+
|
|
28
|
+
def handle(
|
|
29
|
+
self, request: Request
|
|
30
|
+
) -> Tuple[Optional[Response], Optional[RequestError]]:
|
|
31
|
+
"""
|
|
32
|
+
Retry the request if the previous handler in the chain returned an error or a response with a status code of 500 or higher.
|
|
33
|
+
|
|
34
|
+
:param Request request: The request to retry.
|
|
35
|
+
:return: The response and any error that occurred.
|
|
36
|
+
:rtype: Tuple[Optional[Response], Optional[RequestError]]
|
|
37
|
+
:raises RequestError: If the handler chain is incomplete.
|
|
38
|
+
"""
|
|
39
|
+
if self._next_handler is None:
|
|
40
|
+
raise RequestError("Handler chain is incomplete")
|
|
41
|
+
|
|
42
|
+
response, error = self._next_handler.handle(request)
|
|
43
|
+
|
|
44
|
+
try_count = 0
|
|
45
|
+
while try_count < self._max_attempts and self._should_retry(error):
|
|
46
|
+
jitter = random.uniform(0.5, 1.5)
|
|
47
|
+
delay = self._delay_in_milliseconds * (2**try_count) * jitter / 1000
|
|
48
|
+
sleep(delay)
|
|
49
|
+
response, error = self._next_handler.handle(request)
|
|
50
|
+
try_count += 1
|
|
51
|
+
|
|
52
|
+
return response, error
|
|
53
|
+
|
|
54
|
+
def _should_retry(self, error: Optional[RequestError]) -> bool:
|
|
55
|
+
"""
|
|
56
|
+
Determine whether the request should be retried.
|
|
57
|
+
|
|
58
|
+
:param Optional[Response] response: The response from the previous handler.
|
|
59
|
+
:param Optional[RequestError] error: The error from the previous handler.
|
|
60
|
+
:return: True if the request should be retried, False otherwise.
|
|
61
|
+
:rtype: bool
|
|
62
|
+
"""
|
|
63
|
+
if not error:
|
|
64
|
+
return False
|
|
65
|
+
return error.status_code == 408 or error.status_code >= 500
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from .handlers.base_handler import BaseHandler
|
|
3
|
+
from ..transport.request import Request
|
|
4
|
+
from ..transport.response import Response
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RequestChain:
|
|
8
|
+
"""
|
|
9
|
+
Class representing a chain of request handlers.
|
|
10
|
+
Handlers are added to the chain and the request is passed through each handler in the order they were added.
|
|
11
|
+
|
|
12
|
+
:ivar Optional[BaseHandler] _head: The first handler in the chain.
|
|
13
|
+
:ivar Optional[BaseHandler] _tail: The last handler in the chain.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""
|
|
18
|
+
Initialize a new instance of RequestChain.
|
|
19
|
+
"""
|
|
20
|
+
self._head: Optional[BaseHandler] = None
|
|
21
|
+
self._tail: Optional[BaseHandler] = None
|
|
22
|
+
|
|
23
|
+
def add_handler(self, handler: BaseHandler) -> "RequestChain":
|
|
24
|
+
"""
|
|
25
|
+
Add a handler to the chain.
|
|
26
|
+
|
|
27
|
+
:param BaseHandler handler: The handler to add.
|
|
28
|
+
:return: The current instance of RequestChain to allow for method chaining.
|
|
29
|
+
:rtype: RequestChain
|
|
30
|
+
"""
|
|
31
|
+
if self._head is None:
|
|
32
|
+
self._head = handler
|
|
33
|
+
self._tail = handler
|
|
34
|
+
else:
|
|
35
|
+
self._tail.set_next(handler)
|
|
36
|
+
self._tail = handler
|
|
37
|
+
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
def send(self, request: Request) -> Response:
|
|
41
|
+
"""
|
|
42
|
+
Send the request through the chain of handlers.
|
|
43
|
+
|
|
44
|
+
:param Request request: The request to send.
|
|
45
|
+
:return: The response from the request.
|
|
46
|
+
:rtype: Response
|
|
47
|
+
:raises RuntimeError: If the RequestChain is empty.
|
|
48
|
+
"""
|
|
49
|
+
if self._head is not None:
|
|
50
|
+
response, error = self._head.handle(request)
|
|
51
|
+
|
|
52
|
+
if error is not None:
|
|
53
|
+
raise error
|
|
54
|
+
|
|
55
|
+
return response
|
|
56
|
+
else:
|
|
57
|
+
raise RuntimeError("RequestChain is empty")
|
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from .utils import extract_original_data
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Request:
|
|
6
|
+
"""
|
|
7
|
+
A simple HTTP request builder class using the requests library.
|
|
8
|
+
|
|
9
|
+
Example Usage:
|
|
10
|
+
```python
|
|
11
|
+
# Create a Request object
|
|
12
|
+
request = Request()
|
|
13
|
+
|
|
14
|
+
# Set request parameters
|
|
15
|
+
request.set_url('https://yourendpoint.com/') \
|
|
16
|
+
.set_method('GET') \
|
|
17
|
+
.set_headers({'Content-Type': 'application/json'}) \
|
|
18
|
+
.set_body(None) # For GET requests, the body should be None
|
|
19
|
+
|
|
20
|
+
# Send the HTTP request
|
|
21
|
+
response = request.send()
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
:ivar str url: The URL of the API endpoint.
|
|
25
|
+
:ivar str method: The HTTP method for the request.
|
|
26
|
+
:ivar dict headers: Dictionary of headers to include in the request.
|
|
27
|
+
:ivar Any body: Request body.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self.url = None
|
|
32
|
+
self.method = None
|
|
33
|
+
self.headers = None
|
|
34
|
+
self.body = None
|
|
35
|
+
|
|
36
|
+
def set_url(self, url: str) -> "Request":
|
|
37
|
+
"""
|
|
38
|
+
Set the URL of the API endpoint.
|
|
39
|
+
|
|
40
|
+
:param str url: The URL of the API endpoint.
|
|
41
|
+
:return: The updated Request object.
|
|
42
|
+
:rtype: Request
|
|
43
|
+
"""
|
|
44
|
+
self.url = url
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def set_headers(self, headers: dict) -> "Request":
|
|
48
|
+
"""
|
|
49
|
+
Set the headers for the HTTP request.
|
|
50
|
+
|
|
51
|
+
:param dict headers: Dictionary of headers to include in the request.
|
|
52
|
+
:return: The updated Request object.
|
|
53
|
+
:rtype: Request
|
|
54
|
+
"""
|
|
55
|
+
self.headers = headers
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def set_method(self, method: str) -> "Request":
|
|
59
|
+
"""
|
|
60
|
+
Set the HTTP method for the request.
|
|
61
|
+
|
|
62
|
+
:param str method: The HTTP method (e.g., 'GET', 'POST', 'PUT', 'DELETE', etc.).
|
|
63
|
+
:return: The updated Request object.
|
|
64
|
+
:rtype: Request
|
|
65
|
+
"""
|
|
66
|
+
self.method = method
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def set_body(self, body: Any, content_type: str = "application/json") -> "Request":
|
|
70
|
+
"""
|
|
71
|
+
Set the request body (e.g., JSON payload).
|
|
72
|
+
|
|
73
|
+
:param Any body: Request body.
|
|
74
|
+
:param str content_type: The content type of the request body. Default is "application/json".
|
|
75
|
+
:return: The updated Request object.
|
|
76
|
+
:rtype: Request
|
|
77
|
+
"""
|
|
78
|
+
self.body = extract_original_data(body)
|
|
79
|
+
self.headers["Content-Type"] = content_type
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
def __str__(self) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Return a string representation of the Request object.
|
|
85
|
+
|
|
86
|
+
:return: A string representation of the Request object.
|
|
87
|
+
:rtype: str
|
|
88
|
+
"""
|
|
89
|
+
return f"Request(url={self.url}, method={self.method}, headers={self.headers}, body={self.body})"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from .response import Response
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RequestError(IOError):
|
|
6
|
+
"""
|
|
7
|
+
Class representing a Request Error.
|
|
8
|
+
|
|
9
|
+
:ivar bool is_http_error: Indicates if the error is an HTTP error.
|
|
10
|
+
:ivar int status_code: The status code of the HTTP error.
|
|
11
|
+
:ivar Optional[Response] response: The response associated with the error.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
message: str,
|
|
17
|
+
status_code: Optional[int] = None,
|
|
18
|
+
response: Optional[Response] = None,
|
|
19
|
+
stack: Optional["RequestError"] = None,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Initialize a new instance of RequestError.
|
|
23
|
+
|
|
24
|
+
:param str message: The error message.
|
|
25
|
+
:param Optional[int] status_code: The status code of the HTTP error.
|
|
26
|
+
:param Optional[Response] response: The response associated with the error.
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(message)
|
|
29
|
+
self.response = response
|
|
30
|
+
self.stack = stack
|
|
31
|
+
|
|
32
|
+
if status_code is not None:
|
|
33
|
+
self.status_code = status_code
|
|
34
|
+
self.is_http_error = True
|
|
35
|
+
else:
|
|
36
|
+
self.status_code = -1
|
|
37
|
+
self.is_http_error = False
|
|
38
|
+
|
|
39
|
+
def __str__(self):
|
|
40
|
+
"""
|
|
41
|
+
Get the string representation of the error.
|
|
42
|
+
|
|
43
|
+
:return: The string representation of the error.
|
|
44
|
+
:rtype: str
|
|
45
|
+
"""
|
|
46
|
+
error_stack = []
|
|
47
|
+
current_error = self
|
|
48
|
+
while current_error is not None:
|
|
49
|
+
error_stack.append(
|
|
50
|
+
f"Error: {super().__str__()}, Status Code: {current_error.status_code}"
|
|
51
|
+
)
|
|
52
|
+
current_error = current_error.stack
|
|
53
|
+
return "\n".join(error_stack)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from requests import Response as RequestsResponse
|
|
2
|
+
from urllib.parse import parse_qs
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Response:
|
|
6
|
+
"""
|
|
7
|
+
A simple HTTP response wrapper class using the requests library.
|
|
8
|
+
|
|
9
|
+
:ivar int status_code: The status code of the HTTP response.
|
|
10
|
+
:ivar dict headers: The headers of the HTTP response.
|
|
11
|
+
:ivar str body: The body of the HTTP response.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, response: RequestsResponse):
|
|
15
|
+
"""
|
|
16
|
+
Initializes a Response object.
|
|
17
|
+
|
|
18
|
+
:param RequestsResponse response: The requests.Response object.
|
|
19
|
+
"""
|
|
20
|
+
self.status_code = response.status_code
|
|
21
|
+
self.headers = response.headers
|
|
22
|
+
self.body = self._get_response_body(response)
|
|
23
|
+
|
|
24
|
+
def __str__(self) -> str:
|
|
25
|
+
"""
|
|
26
|
+
Return a string representation of the Response object.
|
|
27
|
+
|
|
28
|
+
:return: A string representation of the Response object.
|
|
29
|
+
:rtype: str
|
|
30
|
+
"""
|
|
31
|
+
return f"Response(status_code={self.status_code}, headers={self.headers}, body={self.body})"
|
|
32
|
+
|
|
33
|
+
def _get_response_body(self, response: RequestsResponse) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Extracts the response body from a given HTTP response.
|
|
36
|
+
|
|
37
|
+
This method attempts to parse the response body based on its content type.
|
|
38
|
+
If the content type is JSON, it tries to parse the body as JSON.
|
|
39
|
+
If the content type is text or XML, it returns the raw text.
|
|
40
|
+
If the content type is 'application/x-www-form-urlencoded', it parses the body as a query string.
|
|
41
|
+
For all other content types, it returns the raw binary content.
|
|
42
|
+
|
|
43
|
+
:param RequestsResponse response: The HTTP response received from a request.
|
|
44
|
+
:return: The parsed response body.
|
|
45
|
+
:rtype: str or dict or bytes
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
return response.json()
|
|
49
|
+
except ValueError:
|
|
50
|
+
content_type = response.headers.get("Content-Type", "").lower()
|
|
51
|
+
if "text/" in content_type or content_type == "application/xml":
|
|
52
|
+
return response.text
|
|
53
|
+
elif content_type == "application/x-www-form-urlencoded":
|
|
54
|
+
parsed_response = parse_qs(response.text)
|
|
55
|
+
return {k: v[0] for k, v in parsed_response.items()}
|
|
56
|
+
else:
|
|
57
|
+
return response.content
|