File size: 3,261 Bytes
48ae4e0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
"""
Async HTTP Client with Retry Logic
"""

import aiohttp
import asyncio
from typing import Dict, Optional, Any
from datetime import datetime
import logging

logger = logging.getLogger(__name__)


class APIClient:
    def __init__(self, timeout: int = 10, max_retries: int = 3):
        self.timeout = aiohttp.ClientTimeout(total=timeout)
        self.max_retries = max_retries
        self.session: Optional[aiohttp.ClientSession] = None

    async def __aenter__(self):
        self.session = aiohttp.ClientSession(timeout=self.timeout)
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            await self.session.close()

    async def get(
        self,
        url: str,
        headers: Optional[Dict] = None,
        params: Optional[Dict] = None,
        retry_count: int = 0
    ) -> Dict[str, Any]:
        """Make GET request with retry logic"""
        start_time = datetime.utcnow()

        try:
            async with self.session.get(url, headers=headers, params=params) as response:
                elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)

                # Try to parse JSON response
                try:
                    data = await response.json()
                except:
                    data = await response.text()

                return {
                    "success": response.status == 200,
                    "status_code": response.status,
                    "data": data,
                    "response_time_ms": elapsed_ms,
                    "error": None if response.status == 200 else {
                        "type": "http_error",
                        "message": f"HTTP {response.status}"
                    }
                }

        except asyncio.TimeoutError:
            elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)

            if retry_count < self.max_retries:
                logger.warning(f"Timeout for {url}, retrying ({retry_count + 1}/{self.max_retries})")
                await asyncio.sleep(2 ** retry_count)  # Exponential backoff
                return await self.get(url, headers, params, retry_count + 1)

            return {
                "success": False,
                "status_code": 0,
                "data": None,
                "response_time_ms": elapsed_ms,
                "error": {"type": "timeout", "message": "Request timeout"}
            }

        except aiohttp.ClientError as e:
            elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)

            return {
                "success": False,
                "status_code": 0,
                "data": None,
                "response_time_ms": elapsed_ms,
                "error": {"type": "client_error", "message": str(e)}
            }

        except Exception as e:
            elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)

            logger.error(f"Unexpected error for {url}: {e}")

            return {
                "success": False,
                "status_code": 0,
                "data": None,
                "response_time_ms": elapsed_ms,
                "error": {"type": "unknown", "message": str(e)}
            }