1+ import time
2+ import pytest
13import requests
4+ from requests .adapters import HTTPAdapter
5+ from urllib3 .util .retry import Retry
6+ from config import settings
7+
28
39class APIClient :
4- def __init__ (self , base_url ):
5- self .base_url = base_url
10+ """Cliente API com suporte a retry, rate-limit e formatação de URLs."""
11+
12+ def __init__ (self , base_url : str , connect_timeout : float , read_timeout : float , retry_total : int , rate_sleep : float ):
13+ """
14+ Inicializa o cliente API.
15+
16+ Args:
17+ base_url: URL base da API
18+ connect_timeout: Timeout de conexão em segundos
19+ read_timeout: Timeout de leitura em segundos
20+ retry_total: Total de tentativas para requisições
21+ rate_sleep: Tempo de espera entre requisições (rate-limit)
22+ """
23+ self .base_url = base_url .rstrip ("/" )
24+ self .session = requests .Session ()
25+ retry = Retry (
26+ total = retry_total ,
27+ backoff_factor = 0.5 ,
28+ status_forcelist = [429 , 500 , 502 , 503 , 504 ],
29+ allowed_methods = ["GET" , "POST" , "PUT" , "DELETE" ],
30+ respect_retry_after_header = True ,
31+ )
32+ adapter = HTTPAdapter (
33+ max_retries = retry , pool_connections = 10 , pool_maxsize = 10 )
34+ self .session .mount ("https://" , adapter )
35+ self .session .mount ("http://" , adapter )
36+ self .session .headers .update ({"Accept" : "application/json" })
37+ self .timeout = (connect_timeout , read_timeout )
38+ self .rate_sleep = rate_sleep
39+
40+ def _build_url (self , endpoint : str , ** path_params ) -> str :
41+ """Constrói a URL completa formatando parametros de caminho."""
42+ if path_params :
43+ endpoint = endpoint .format (** path_params )
44+ return f"{ self .base_url } /{ endpoint .lstrip ('/' )} "
45+
46+ def get (self , endpoint : str , params = None , ** kwargs ) -> requests .Response :
47+ """
48+ Faz uma requisição GET.
49+
50+ Args:
51+ endpoint: Endpoint da API (pode ter placeholders como {id})
52+ params: Parâmetros de query
53+ **kwargs: Parâmetros de caminho como id=123
54+
55+ Returns:
56+ Response object
57+ """
58+ url = self ._build_url (endpoint , ** kwargs )
59+ time .sleep (self .rate_sleep )
60+ return self .session .get (url , params = params , timeout = self .timeout )
61+
62+ def post (self , endpoint : str , data = None , ** kwargs ) -> requests .Response :
63+ """
64+ Faz uma requisição POST.
65+
66+ Args:
67+ endpoint: Endpoint da API
68+ data: Dados a enviar no body (será convertido para JSON)
69+ **kwargs: Parâmetros de caminho
70+
71+ Returns:
72+ Response object
73+ """
74+ url = self ._build_url (endpoint , ** kwargs )
75+ time .sleep (self .rate_sleep )
76+ return self .session .post (url , json = data , timeout = self .timeout )
77+
78+ def put (self , endpoint : str , data = None , ** kwargs ) -> requests .Response :
79+ """
80+ Faz uma requisição PUT.
81+
82+ Args:
83+ endpoint: Endpoint da API
84+ data: Dados a enviar no body (será convertido para JSON)
85+ **kwargs: Parâmetros de caminho
86+
87+ Returns:
88+ Response object
89+ """
90+ url = self ._build_url (endpoint , ** kwargs )
91+ time .sleep (self .rate_sleep )
92+ return self .session .put (url , json = data , timeout = self .timeout )
693
7- def get (self , endpoint , params = None , ** kwargs ):
94+ def delete (self , endpoint : str , ** kwargs ) -> requests . Response :
895 """
9- Makes a GET request .
10-
96+ Faz uma requisição DELETE .
97+
1198 Args:
12- endpoint: API endpoint (can have {id} placeholders)
13- params: Query parameters
14- **kwargs: Path parameters like id=123
15- """
16- url = endpoint .format (** kwargs ) if kwargs else endpoint
17- response = requests .get (f"{ self .base_url } { url } " , params = params )
18- return response
19-
20- def post (self , endpoint , data = None ):
21- response = requests .post (f"{ self .base_url } { endpoint } " , json = data )
22- return response
23-
24- def put (self , endpoint , data = None , ** kwargs ):
25- url = endpoint .format (** kwargs ) if kwargs else endpoint
26- response = requests .put (f"{ self .base_url } { url } " , json = data )
27- return response
28-
29- def delete (self , endpoint , ** kwargs ):
30- url = endpoint .format (** kwargs ) if kwargs else endpoint
31- response = requests .delete (f"{ self .base_url } { url } " )
32- return response
99+ endpoint: Endpoint da API
100+ **kwargs: Parâmetros de caminho
101+
102+ Returns:
103+ Response object
104+ """
105+ url = self ._build_url (endpoint , ** kwargs )
106+ time .sleep (self .rate_sleep )
107+ return self .session .delete (url , timeout = self .timeout )
108+
109+
110+ @pytest .fixture (scope = "session" )
111+ def base_url () -> str :
112+ return settings .BASE_URL
113+
114+
115+ @pytest .fixture (scope = "session" )
116+ def http () -> APIClient :
117+ return APIClient (
118+ base_url = settings .BASE_URL ,
119+ connect_timeout = settings .CONNECT_TIMEOUT ,
120+ read_timeout = settings .READ_TIMEOUT ,
121+ retry_total = settings .RETRY_TOTAL ,
122+ rate_sleep = settings .RATE_LIMIT_SLEEP ,
123+ )
0 commit comments