Skip to content

Commit

Permalink
Use a custom user agent when issuing HTTP requests. (#43)
Browse files Browse the repository at this point in the history
# Description

This PR introduces adds a `User-Agent` header to `HTTP` requests made
from the API clients, this will allow us to identify which requests
originate from the SDK.

The `User-Agent` follows the format defined
[here](https://www.rfc-editor.org/rfc/rfc9110.html#name-user-agent).

This PR doesn't have `GDPR` implications as there's no personal data
used and the user agent cannot be used to identify a specific user or
clusters of users.

# References

- https://www.rfc-editor.org/rfc/rfc9110.html#name-user-agent
-
https://www.python-httpx.org/advanced/clients/#sharing-configuration-across-requests
-
https://www.python-httpx.org/advanced/clients/#merging-of-configuration
  • Loading branch information
cipherself authored Mar 7, 2024
1 parent dd84243 commit e62466a
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 1 deletion.
13 changes: 12 additions & 1 deletion src/enlyze/api_clients/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@
import httpx
from pydantic import BaseModel, ValidationError

from enlyze._version import VERSION
from enlyze.auth import TokenAuth
from enlyze.constants import HTTPX_TIMEOUT
from enlyze.constants import HTTPX_TIMEOUT, USER_AGENT
from enlyze.errors import EnlyzeError, InvalidTokenError

USER_AGENT_NAME_VERSION_SEPARATOR = "/"


@cache
def _construct_user_agent(
*, user_agent: str = USER_AGENT, version: str = VERSION
) -> str:
return f"{user_agent}{USER_AGENT_NAME_VERSION_SEPARATOR}{version}"


class ApiBaseModel(BaseModel):
"""Base class for ENLYZE platform API object models using pydantic
Expand Down Expand Up @@ -60,6 +70,7 @@ def __init__(
auth=TokenAuth(token),
base_url=httpx.URL(base_url),
timeout=timeout,
headers={"user-agent": _construct_user_agent()},
)

@cache
Expand Down
3 changes: 3 additions & 0 deletions src/enlyze/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
#: The maximum number of variables that can be used in a single request when querying
#: timeseries data.
MAXIMUM_NUMBER_OF_VARIABLES_PER_TIMESERIES_REQUEST = 100

#: The user agent that the SDK identifies itself as when making HTTP requests
USER_AGENT = "enlyze-python"
44 changes: 44 additions & 0 deletions tests/enlyze/api_clients/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
from hypothesis import HealthCheck, given, settings
from hypothesis import strategies as st

from enlyze._version import VERSION
from enlyze.api_clients.base import (
USER_AGENT_NAME_VERSION_SEPARATOR,
ApiBaseClient,
ApiBaseModel,
PaginatedResponseBaseModel,
_construct_user_agent,
)
from enlyze.constants import USER_AGENT
from enlyze.errors import EnlyzeError, InvalidTokenError


Expand Down Expand Up @@ -83,6 +87,46 @@ def base_client(auth_token, string_model, base_url):
yield client


@pytest.fixture
def custom_user_agent():
return "custom-user-agent"


@pytest.fixture
def custom_user_agent_version():
return "3.4.5"


class TestConstructUserAgent:
def test__construct_user_agent_with_defaults(self):
ua, version = _construct_user_agent().split(USER_AGENT_NAME_VERSION_SEPARATOR)
assert ua == USER_AGENT
assert version == VERSION

def test__construct_user_agent_custom_agent(self, custom_user_agent):
ua, version = _construct_user_agent(user_agent=custom_user_agent).split(
USER_AGENT_NAME_VERSION_SEPARATOR
)
assert ua == custom_user_agent
assert version == VERSION

def test__construct_user_agent_custom_version(self, custom_user_agent_version):
ua, version = _construct_user_agent(version=custom_user_agent_version).split(
USER_AGENT_NAME_VERSION_SEPARATOR
)
assert ua == USER_AGENT
assert version == custom_user_agent_version

def test__construct_user_agent_custom_agent_and_version(
self, custom_user_agent, custom_user_agent_version
):
ua, version = _construct_user_agent(
user_agent=custom_user_agent, version=custom_user_agent_version
).split(USER_AGENT_NAME_VERSION_SEPARATOR)
assert ua == custom_user_agent
assert version == custom_user_agent_version


@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
@given(
token=st.text(string.printable, min_size=1),
Expand Down

0 comments on commit e62466a

Please sign in to comment.