fix: Recreate httpx client when closed in telegram bot API client
The singleton BackendAPIClient was failing with "Cannot send a request, as the client has been closed" error when used after async context manager closed it in linking.py. Changed all client checks from `if not self.client` to `if not self.client or self.client.is_closed` to properly recreate the AsyncClient when needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -131,7 +131,7 @@ class BackendAPIClient:
|
|||||||
jwt_token = result['access_token']
|
jwt_token = result['access_token']
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Flow A: Auto-linking (no password required)
|
# Flow A: Auto-linking (no password required)
|
||||||
@@ -170,7 +170,7 @@ class BackendAPIClient:
|
|||||||
str: New JWT access token, None if failed
|
str: New JWT access token, None if failed
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.post(
|
response = await self.client.post(
|
||||||
@@ -199,7 +199,7 @@ class BackendAPIClient:
|
|||||||
httpx.HTTPError: On network or HTTP errors
|
httpx.HTTPError: On network or HTTP errors
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.post(
|
response = await self.client.post(
|
||||||
@@ -247,7 +247,7 @@ class BackendAPIClient:
|
|||||||
httpx.HTTPError: On network or HTTP errors
|
httpx.HTTPError: On network or HTTP errors
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.post(
|
response = await self.client.post(
|
||||||
@@ -309,7 +309,7 @@ class BackendAPIClient:
|
|||||||
List of company dicts with id, nume_firma, cui, etc.
|
List of company dicts with id, nume_firma, cui, etc.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
@@ -350,7 +350,7 @@ class BackendAPIClient:
|
|||||||
Includes _cache_hit and _response_time_ms metadata
|
Includes _cache_hit and _response_time_ms metadata
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Add cache metadata header for Telegram Bot
|
# Add cache metadata header for Telegram Bot
|
||||||
@@ -385,7 +385,7 @@ class BackendAPIClient:
|
|||||||
Dict with treasury breakdown data (accounts by type)
|
Dict with treasury breakdown data (accounts by type)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Add cache metadata header for Telegram Bot
|
# Add cache metadata header for Telegram Bot
|
||||||
@@ -421,7 +421,7 @@ class BackendAPIClient:
|
|||||||
Dict with detailed data (list of clients/suppliers with balances)
|
Dict with detailed data (list of clients/suppliers with balances)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Add cache metadata header for Telegram Bot
|
# Add cache metadata header for Telegram Bot
|
||||||
@@ -457,7 +457,7 @@ class BackendAPIClient:
|
|||||||
Dict with maturity data (in_term, overdue, total)
|
Dict with maturity data (in_term, overdue, total)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Add cache metadata header for Telegram Bot
|
# Add cache metadata header for Telegram Bot
|
||||||
@@ -491,7 +491,7 @@ class BackendAPIClient:
|
|||||||
Dict with performance data (incasari_total, plati_total, net)
|
Dict with performance data (incasari_total, plati_total, net)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Add cache metadata header for Telegram Bot
|
# Add cache metadata header for Telegram Bot
|
||||||
@@ -527,7 +527,7 @@ class BackendAPIClient:
|
|||||||
Dict with monthly flows (months, incasari, plati arrays)
|
Dict with monthly flows (months, incasari, plati arrays)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Add cache metadata header for Telegram Bot
|
# Add cache metadata header for Telegram Bot
|
||||||
@@ -563,7 +563,7 @@ class BackendAPIClient:
|
|||||||
Dict with trends data including periods, clienti_incasat, furnizori_achitat arrays
|
Dict with trends data including periods, clienti_incasat, furnizori_achitat arrays
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
# Add cache metadata header for Telegram Bot
|
# Add cache metadata header for Telegram Bot
|
||||||
@@ -611,7 +611,7 @@ class BackendAPIClient:
|
|||||||
List of invoice dicts
|
List of invoice dicts
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
params = {"company": company_id}
|
params = {"company": company_id}
|
||||||
@@ -656,7 +656,7 @@ class BackendAPIClient:
|
|||||||
Dict with summary (total_count, total_amount, paid, unpaid, etc.)
|
Dict with summary (total_count, total_amount, paid, unpaid, etc.)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
@@ -694,7 +694,7 @@ class BackendAPIClient:
|
|||||||
Dict with treasury data (cash_balance, incoming, outgoing, etc.)
|
Dict with treasury data (cash_balance, incoming, outgoing, etc.)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
@@ -739,7 +739,7 @@ class BackendAPIClient:
|
|||||||
bytes: File content, None if failed
|
bytes: File content, None if failed
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
request_data = {
|
request_data = {
|
||||||
@@ -784,7 +784,7 @@ class BackendAPIClient:
|
|||||||
bool: True if successful
|
bool: True if successful
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
request_data = {}
|
request_data = {}
|
||||||
@@ -823,7 +823,7 @@ class BackendAPIClient:
|
|||||||
bool: True if successful
|
bool: True if successful
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.post(
|
response = await self.client.post(
|
||||||
@@ -854,7 +854,7 @@ class BackendAPIClient:
|
|||||||
Dict with cache stats including 'user_enabled' field
|
Dict with cache stats including 'user_enabled' field
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
@@ -881,7 +881,7 @@ class BackendAPIClient:
|
|||||||
bool: True if backend is healthy
|
bool: True if backend is healthy
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client or self.client.is_closed:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
response = await self.client.get("/api/telegram/health")
|
response = await self.client.get("/api/telegram/health")
|
||||||
|
|||||||
Reference in New Issue
Block a user