Skip to content

Commit 12ba685

Browse files
committed
fix retry limit token
1 parent f1b37a5 commit 12ba685

4 files changed

Lines changed: 75 additions & 27 deletions

File tree

sinch/core/adapters/requests_http_transport.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self, sinch):
99
super().__init__(sinch)
1010
self.http_session = requests.Session()
1111

12-
def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
12+
def send(self, endpoint: HTTPEndpoint) -> HTTPResponse:
1313
request_data: HttpRequest = self.prepare_request(endpoint)
1414
request_data: HttpRequest = self.authenticate(endpoint, request_data)
1515

@@ -34,11 +34,8 @@ def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
3434
f"and body: {response_body} from URL: {request_data.url}"
3535
)
3636

37-
return self.handle_response(
38-
endpoint=endpoint,
39-
http_response=HTTPResponse(
40-
status_code=response.status_code,
41-
body=response_body,
42-
headers=response.headers
43-
)
37+
return HTTPResponse(
38+
status_code=response.status_code,
39+
body=response_body,
40+
headers=response.headers
4441
)

sinch/core/ports/http_transport.py

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,59 @@
1010

1111

1212
class HTTPTransport(ABC):
13+
"""Base class for HTTP transports.
14+
15+
Subclasses implement ``send`` to perform the raw HTTP call.
16+
The public ``request`` method adds cross-cutting concerns on top:
17+
authentication, logging hooks, and automatic token refresh on 401.
18+
"""
19+
1320
def __init__(self, sinch):
1421
self.sinch = sinch
1522

23+
# ------------------------------------------------------------------
24+
# Subclass contract
25+
# ------------------------------------------------------------------
26+
1627
@abstractmethod
28+
def send(self, endpoint: HTTPEndpoint) -> HTTPResponse:
29+
"""Execute a single HTTP round-trip and return the response.
30+
31+
Implementations must prepare the request, authenticate, perform the
32+
HTTP call, deserialize the response, and return an ``HTTPResponse``.
33+
They should **not** handle token refresh — that is done by
34+
``request``.
35+
"""
36+
37+
# ------------------------------------------------------------------
38+
# Public API
39+
# ------------------------------------------------------------------
40+
1741
def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
18-
pass
42+
"""Send a request with automatic OAuth token refresh on 401.
43+
44+
If the server responds with 401 *and* the token is detected as
45+
expired, the token is invalidated and **one** retry is attempted
46+
with a fresh token. A second consecutive 401 is handed straight
47+
to the endpoint's error handler — no further retries.
48+
"""
49+
http_response = self.send(endpoint)
50+
51+
if self._should_refresh_token(endpoint, http_response):
52+
self.sinch.configuration.token_manager.handle_invalid_token(
53+
http_response
54+
)
55+
if (
56+
self.sinch.configuration.token_manager.token_state
57+
== TokenState.EXPIRED
58+
):
59+
http_response = self.send(endpoint)
60+
61+
return endpoint.handle_response(http_response)
62+
63+
# ------------------------------------------------------------------
64+
# Internals
65+
# ------------------------------------------------------------------
1966

2067
def authenticate(self, endpoint, request_data):
2168
if endpoint.HTTP_AUTHENTICATION in (HTTPAuthentication.BASIC.value, HTTPAuthentication.OAUTH.value):
@@ -83,10 +130,7 @@ def deserialize_json_response(response):
83130
response_body = response.json()
84131
except ValueError as err:
85132
raise SinchException(
86-
message=(
87-
"Error while parsing json response.",
88-
err.msg
89-
),
133+
message=f"Error while parsing json response. {err}",
90134
is_from_server=True,
91135
response=response
92136
)
@@ -95,10 +139,11 @@ def deserialize_json_response(response):
95139

96140
return response_body
97141

98-
def handle_response(self, endpoint: HTTPEndpoint, http_response: HTTPResponse):
99-
if http_response.status_code == 401 and endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.OAUTH.value:
100-
self.sinch.configuration.token_manager.handle_invalid_token(http_response)
101-
if self.sinch.configuration.token_manager.token_state == TokenState.EXPIRED:
102-
return self.request(endpoint=endpoint)
103-
104-
return endpoint.handle_response(http_response)
142+
@staticmethod
143+
def _should_refresh_token(endpoint, http_response):
144+
"""Return True when a 401 response should trigger a token refresh."""
145+
return (
146+
http_response.status_code == 401
147+
and endpoint.HTTP_AUTHENTICATION
148+
== HTTPAuthentication.OAUTH.value
149+
)

sinch/domains/conversation/api/v1/internal/base/conversation_endpoint.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,13 @@ def process_response_model(
107107

108108
def handle_response(self, response: HTTPResponse):
109109
if response.status_code >= 400:
110-
error = (response.body or {}).get('error', {})
111-
message = error.get('message', '')
112-
status = error.get('status', '')
113-
error_message = f"{message} {status}".strip() or f"Error {response.status_code}"
110+
error = (response.body or {}).get("error", {})
111+
message = error.get("message", "")
112+
status = error.get("status", "")
113+
error_message = (
114+
f"{message} {status}".strip()
115+
or f"Error {response.status_code}"
116+
)
114117
raise ConversationException(
115118
message=error_message,
116119
response=response,

sinch/domains/numbers/api/v1/internal/base/numbers_endpoint.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,12 @@ def handle_response(self, response: HTTPResponse):
9090
)
9191

9292
if response.status_code >= 400:
93-
message = error_data.get('message', '')
94-
status = error_data.get('status', '')
95-
error_message = f"{message} {status}".strip() or f"Error {response.status_code}"
93+
message = error_data.get("message", "")
94+
status = error_data.get("status", "")
95+
error_message = (
96+
f"{message} {status}".strip()
97+
or f"Error {response.status_code}"
98+
)
9699
raise NumbersException(
97100
message=error_message,
98101
response=response,

0 commit comments

Comments
 (0)