22import logging
33import warnings
44from pathlib import Path
5- from typing import Any , Dict , List , Optional , Tuple , Union , cast
5+ from typing import Any , Dict , List , Literal , Optional , Tuple , Union
66
77import requests
88
@@ -16,6 +16,59 @@ def get_http_auth(environment: Environment) -> Optional[Tuple[str, str]]:
1616 return ("off" , "off" ) if environment is Environment .net else None
1717
1818
19+ def send_request (
20+ url : str ,
21+ api_config : APIConfig ,
22+ method : Literal ["get" , "post" , "delete" , "head" , "put" ] = "get" ,
23+ return_none_on_404 : bool = False ,
24+ ** request_args ,
25+ ) -> requests .Response | None :
26+ """Send an HTTP request to the given URL.
27+
28+ :param url: the URL to send the request to.
29+ :param api_config: the API configuration.
30+ :param method: type of HTTP request. Defaults to "get".
31+ :param return_none_on_404: if True, None is returned if the response
32+ status code is 404, defaults to False.
33+ :param request_args: Optional arguments that requests.Session.request takes
34+ :return: the API response.
35+ """
36+ # Raise some errors early on unknown methods
37+ if method .lower () not in ("get" , "post" , "delete" , "head" , "put" ):
38+ raise NotImplementedError (f'The HTTP method "{ method } " is not implemented.' )
39+ http_session_call = getattr (http_session , method .lower ())
40+
41+ # Build up the request
42+ request : JSONType = request_args | {
43+ "url" : url ,
44+ "headers" : request_args .get ("headers" , dict ())
45+ | {"User-Agent" : api_config .user_agent },
46+ "timeout" : api_config .timeout ,
47+ }
48+
49+ # Handle authentication
50+ if api_config .username and api_config .password :
51+ key = "json" if request_args .get ("json" ) else "data"
52+ request [key ] = request_args .get (key , dict ()) | {
53+ "user_id" : api_config .username ,
54+ "password" : api_config .password ,
55+ }
56+ elif api_config .session_cookie :
57+ request ["cookies" ] = request_args .get ("cookies" , dict ()) | {
58+ "session" : api_config .session_cookie ,
59+ }
60+ if "auth" not in request :
61+ request ["auth" ] = get_http_auth (api_config .environment )
62+
63+ # Handle request and return data
64+ r = http_session_call (** request )
65+ if r .status_code == 404 and return_none_on_404 :
66+ return None
67+ r .raise_for_status ()
68+ return r
69+
70+
71+ @warnings .deprecated ("Use send_request() instead." )
1972def send_get_request (
2073 url : str ,
2174 api_config : APIConfig ,
@@ -32,62 +85,42 @@ def send_get_request(
3285 status code is 404, defaults to False
3386 :return: the API response
3487 """
35- r = http_session . get (
88+ r = send_request (
3689 url ,
90+ api_config = api_config ,
3791 params = params ,
38- headers = {"User-Agent" : api_config .user_agent },
39- timeout = api_config .timeout ,
92+ return_none_on_404 = return_none_on_404 ,
4093 auth = auth ,
94+ method = "get" ,
4195 )
42- if r . status_code == 404 and return_none_on_404 :
96+ if r is None :
4397 return None
44- r . raise_for_status ()
45- return r .json ()
98+ else :
99+ return r .json ()
46100
47101
102+ @warnings .deprecated ("Use send_request() instead." )
48103def send_form_urlencoded_post_request (
49104 url : str , body : Dict [str , Any ], api_config : APIConfig
50105) -> requests .Response :
51- cookies = None
52- if api_config .username and api_config .password :
53- body ["user_id" ] = api_config .username
54- body ["password" ] = api_config .password
55- elif api_config .session_cookie :
56- cookies = {
57- "session" : api_config .session_cookie ,
58- }
59- r = http_session .post (
106+ return send_request (
60107 url ,
61108 data = body ,
62- headers = {"User-Agent" : api_config .user_agent },
63- timeout = api_config .timeout ,
64- auth = get_http_auth (api_config .environment ),
65- cookies = cookies ,
109+ api_config = api_config ,
110+ method = "post" ,
66111 )
67- r .raise_for_status ()
68- return r
69112
70113
114+ @warnings .deprecated ("Use send_request() instead." )
71115def send_json_post_request (
72116 url : str , body : JSONType , api_config : APIConfig
73117) -> requests .Response :
74- cookies = None
75- if api_config .username and api_config .password :
76- body ["user_id" ] = api_config .username
77- body ["password" ] = api_config .password
78- elif api_config .session_cookie :
79- cookies = {
80- "session" : api_config .session_cookie ,
81- }
82- r = http_session .post (
118+ return send_request (
83119 url ,
84120 json = body ,
85- headers = {"User-Agent" : api_config .user_agent },
86- timeout = api_config .timeout ,
87- auth = get_http_auth (api_config .environment ),
88- cookies = cookies ,
121+ api_config = api_config ,
122+ method = "post" ,
89123 )
90- return r
91124
92125
93126class RobotoffResource :
@@ -136,14 +169,12 @@ def get(
136169 """
137170 facet = Facet .from_str_or_enum (facet_name )
138171 facet_plural = facet .value .replace ("_" , "-" )
139- resp = send_get_request (
172+ return send_request (
140173 url = f"{ self .base_url } /facets/{ facet_plural } " ,
141174 params = {"json" : "1" , "page" : page , "page_size" : page_size , ** kwargs },
142175 api_config = self .api_config ,
143- auth = get_http_auth (self .api_config .environment ),
144- )
145- resp = cast (JSONType , resp )
146- return resp
176+ method = "get" ,
177+ ).json ()
147178
148179 def get_products (
149180 self ,
@@ -175,14 +206,12 @@ def get_products(
175206 if sort_by is not None :
176207 params ["sort_by" ] = sort_by
177208
178- resp = send_get_request (
209+ return send_request (
179210 url = f"{ self .base_url } /facets/{ facet_plural } /{ facet_value } " ,
180211 params = params ,
181212 api_config = self .api_config ,
182- auth = get_http_auth (self .api_config .environment ),
183- )
184- resp = cast (JSONType , resp )
185- return resp
213+ method = "get" ,
214+ ).json ()
186215
187216
188217class ProductResource :
@@ -224,13 +253,18 @@ def get(
224253 # https://github.com/openfoodfacts/openfoodfacts-server/issues/1607
225254 url += "?fields={}" .format ("," .join (fields ))
226255
227- resp = send_get_request (
228- url = url , api_config = self .api_config , return_none_on_404 = True
256+ resp = send_request (
257+ url = url ,
258+ api_config = self .api_config ,
259+ return_none_on_404 = True ,
260+ method = "get" ,
229261 )
230262
231263 if resp is None :
232264 # product not found
233265 return None
266+ else :
267+ resp = resp .json ()
234268
235269 if resp ["status" ] == 0 :
236270 # invalid barcode
@@ -267,20 +301,25 @@ def text_search(
267301 if sort_by is not None :
268302 params ["sort_by" ] = sort_by
269303
270- return send_get_request (
304+ return send_request (
271305 url = f"{ self .base_url } /cgi/search.pl" ,
272306 api_config = self .api_config ,
273307 params = params ,
274- auth = get_http_auth ( self . api_config . environment ) ,
275- )
308+ method = "get" ,
309+ ). json ()
276310
277311 def update (self , body : Dict [str , Any ]):
278312 """Create a new product or update an existing one."""
279313 if not body .get ("code" ):
280314 raise ValueError ("missing code from body" )
281315
282316 url = f"{ self .base_url } /cgi/product_jqm2.pl"
283- return send_form_urlencoded_post_request (url , body , self .api_config )
317+ return send_request (
318+ url ,
319+ api_config = self .api_config ,
320+ method = "post" ,
321+ data = body ,
322+ )
284323
285324 def select_image (
286325 self ,
@@ -333,32 +372,18 @@ def select_image(
333372 if image_key is not None :
334373 params ["id" ] = image_key
335374
336- cookies = None
337- if self .api_config .session_cookie :
338- cookies = {
339- "session" : self .api_config .session_cookie ,
340- }
341- elif self .api_config .username :
342- params ["user_id" ] = self .api_config .username
343- params ["password" ] = self .api_config .password
344-
345- if cookies is None and not params .get ("password" ):
375+ if not (self .api_config .session_cookie or self .api_config .password ):
346376 raise ValueError (
347377 "a password or a session cookie is required to select an image"
348378 )
349379
350- r = http_session . post (
380+ return send_request (
351381 url ,
352382 data = params ,
353- headers = {"User-Agent" : self .api_config .user_agent },
354- timeout = self .api_config .timeout ,
355- auth = get_http_auth (self .api_config .environment ),
356- cookies = cookies ,
383+ api_config = self .api_config ,
384+ method = "post" ,
357385 )
358386
359- r .raise_for_status ()
360- return r
361-
362387 def parse_ingredients (
363388 self , text : str , lang : str , timeout : int = 10
364389 ) -> list [JSONType ]:
@@ -549,7 +574,12 @@ def upload_image(
549574 if selected :
550575 data ["selected" ] = selected
551576
552- return send_json_post_request (url , data , api_config )
577+ return send_request (
578+ url ,
579+ json = data ,
580+ api_config = api_config ,
581+ method = "post" ,
582+ )
553583
554584
555585class API :
0 commit comments