Skip to content

Commit

Permalink
feat: Force user agent in API (#184)
Browse files Browse the repository at this point in the history
* fixes: #152 force user agent in API
  • Loading branch information
butteredwaffles committed Dec 1, 2023
1 parent 925e7e5 commit 34024be
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 3 deletions.
3 changes: 2 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ First, instantiate an API object:
from openfoodfacts import API, APIVersion, Country, Environment, Flavor

api = API(
user_agent="<application name>",
username=None,
password=None,
country=Country.world,
Expand All @@ -21,7 +22,7 @@ api = API(
)
```

All parameters are optional, but here is a description of the parameters you can tweak:
All parameters are optional with the exception of user_agent, but here is a description of the parameters you can tweak:

- `username` and `password` are used to provide authentication (required for write requests)
- `country` is used to specify the country, which is used by the API to return product specific to the country or to infer which language to use by default. `world` (all products) is the default value
Expand Down
5 changes: 5 additions & 0 deletions openfoodfacts/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def send_get_request(
r = http_session.get(
url,
params=params,
headers={"User-Agent": api_config.user_agent},
timeout=api_config.timeout,
auth=get_http_auth(api_config.environment),
)
Expand All @@ -37,6 +38,7 @@ def send_for_urlencoded_post_request(
r = http_session.post(
url,
data=body,
headers={"User-Agent": api_config.user_agent},
timeout=api_config.timeout,
auth=get_http_auth(api_config.environment),
cookies=cookies,
Expand Down Expand Up @@ -202,6 +204,7 @@ def select_image(
r = http_session.post(
url,
data=params,
headers={"User-Agent": self.api_config.user_agent},
timeout=self.api_config.timeout,
auth=get_http_auth(self.api_config.environment),
cookies=cookies,
Expand All @@ -214,6 +217,7 @@ def select_image(
class API:
def __init__(
self,
user_agent: str,
username: Optional[str] = None,
password: Optional[str] = None,
country: Union[Country, str] = Country.world,
Expand Down Expand Up @@ -242,6 +246,7 @@ def __init__(
country = Country[country]

self.api_config = APIConfig(
user_agent=user_agent,
country=country,
flavor=Flavor[flavor],
version=APIVersion[version],
Expand Down
7 changes: 7 additions & 0 deletions openfoodfacts/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,7 @@ class Environment(str, enum.Enum):


class APIConfig(BaseModel):
user_agent: str
country: Country = Country.world
environment: Environment = Environment.org
flavor: Flavor = Flavor.off
Expand All @@ -839,6 +840,12 @@ def check_credentials(self):

return self

@model_validator(mode="after")
def check_user_agent(self):
if not isinstance(self.user_agent, str) or not self.user_agent.strip():
raise ValueError("User agent must be a string and cannot be empty.")
return self


class DatasetType(str, enum.Enum):
csv = "csv"
Expand Down
6 changes: 4 additions & 2 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

import openfoodfacts

TEST_USER_AGENT = "test_off_python"


class TestProducts(unittest.TestCase):
def test_get_product(self):
api = openfoodfacts.API(version="v2")
api = openfoodfacts.API(user_agent=TEST_USER_AGENT, version="v2")
code = "1223435"
response_data = {"product": {"code": "1223435"}}
with requests_mock.mock() as mock:
Expand All @@ -20,7 +22,7 @@ def test_get_product(self):
self.assertEqual(res, response_data)

def test_text_search(self):
api = openfoodfacts.API(version="v2")
api = openfoodfacts.API(user_agent=TEST_USER_AGENT, version="v2")
with requests_mock.mock() as mock:
response_data = {"products": ["kinder bueno"], "count": 1}
mock.get(
Expand Down
21 changes: 21 additions & 0 deletions tests/test_api_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest

import pydantic_core

import openfoodfacts


class TestAPIConfig(unittest.TestCase):
def test_valid_user_agent(self):
config = openfoodfacts.APIConfig(user_agent="Valid User Agent")
assert config.user_agent == "Valid User Agent"

def test_invalid_user_agent_type(self):
with self.assertRaises(pydantic_core.ValidationError) as ctx:
openfoodfacts.APIConfig(user_agent=None)
self.assertTrue("valid string" in ctx.exception)

def test_blank_user_agent(self):
with self.assertRaises(pydantic_core.ValidationError) as ctx:
openfoodfacts.APIConfig(user_agent="")
self.assertTrue("cannot be empty" in ctx.exception)

0 comments on commit 34024be

Please sign in to comment.