diff --git a/CHANGELOG.md b/CHANGELOG.md index 11bc25b..9e1fe3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 3.6.3 + +### Changed + +- Use `socket-sdk-python/` as the default User-Agent. Explicit caller + overrides continue to identify the calling application. + ## 3.6.2 ### Fixed: reject retired API credentials before use diff --git a/README.rst b/README.rst index 0ac5262..c4c77a0 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ Initializing the module - **token (str)** - The Socket API Key for your Organization - **timeout (int)** - The number of seconds to wait before failing the connection - **allow_unverified (bool)** - Whether to skip SSL certificate verification (default: False). Set to True for testing with self-signed certificates. -- **user_agent (str, optional)** - Custom User-Agent string to use in API requests. If not provided, defaults to "SocketSDKPython/{version}" +- **user_agent (str, optional)** - Custom User-Agent string to use in API requests. If not provided, defaults to "socket-sdk-python/{version}". Applications such as the Socket CLI can override this with their own product name and version. Supported Functions ------------------- diff --git a/socketdev/core/api.py b/socketdev/core/api.py index 73692f9..07bd61d 100644 --- a/socketdev/core/api.py +++ b/socketdev/core/api.py @@ -17,7 +17,7 @@ APIInsufficientPermissions, APIOrganizationNotAllowed, ) -from socketdev.version import __version__ +from socketdev.user_agent import USER_AGENT from requests.exceptions import Timeout, ConnectionError import time @@ -57,7 +57,7 @@ def do_request( raise APIKeyMissing if headers is None: - user_agent_string = self.user_agent if self.user_agent is not None else f"SocketSDKPython/{__version__}" + user_agent_string = self.user_agent if self.user_agent is not None else USER_AGENT headers = { "Authorization": f"Basic {self.encoded_key}", "User-Agent": user_agent_string, diff --git a/socketdev/user_agent.py b/socketdev/user_agent.py new file mode 100644 index 0000000..3328ecc --- /dev/null +++ b/socketdev/user_agent.py @@ -0,0 +1,4 @@ +from socketdev.version import __version__ + +USER_AGENT_NAME = "socket-sdk-python" +USER_AGENT = f"{USER_AGENT_NAME}/{__version__}" diff --git a/socketdev/version.py b/socketdev/version.py index 4f82193..6a45bf5 100644 --- a/socketdev/version.py +++ b/socketdev/version.py @@ -1 +1 @@ -__version__ = "3.6.2" +__version__ = "3.6.3" diff --git a/tests/unit/test_user_agent.py b/tests/unit/test_user_agent.py new file mode 100644 index 0000000..7dd3632 --- /dev/null +++ b/tests/unit/test_user_agent.py @@ -0,0 +1,66 @@ +import pytest +import requests + +from socketdev import socketdev +from socketdev.version import __version__ + + +@pytest.mark.parametrize("method", ["GET", "POST"]) +@pytest.mark.parametrize( + "user_agent", + [ + None, + "SocketPythonCLI/2.11.7", + "socket-python-cli/2.11.7", + "socket-basics/3.4.1", + "custom-client/1.0.0", + "", + ], +) +def test_prepared_requests_use_sdk_identity_or_explicit_caller( + monkeypatch, method, user_agent +): + captured = [] + + def send(session, request, **kwargs): + captured.append(request) + response = requests.Response() + response.status_code = 200 + response._content = b"{}" + return response + + monkeypatch.setattr(requests.sessions.Session, "send", send) + sdk = socketdev(token="synthetic-test-token", user_agent=user_agent) + files = [("manifest", ("package.json", b"{}"))] if method == "POST" else None + + sdk.api.do_request("organizations", method=method, files=files) + + assert len(captured) == 1 + expected = user_agent if user_agent is not None else f"socket-sdk-python/{__version__}" + assert captured[0].headers["User-Agent"] == expected + assert captured[0].headers["Authorization"].startswith("Basic ") + if files: + assert captured[0].headers["Content-Type"].startswith("multipart/form-data;") + + +def test_endpoint_wrappers_share_the_sdk_identity(monkeypatch): + captured = [] + + def send(session, request, **kwargs): + captured.append(request) + response = requests.Response() + response.status_code = 200 + response._content = b"{}" + return response + + monkeypatch.setattr(requests.sessions.Session, "send", send) + sdk = socketdev(token="synthetic-test-token") + + sdk.org.get() + sdk.basics.get_config("test-org") + + assert len(captured) == 2 + assert all( + request.headers["User-Agent"] == f"socket-sdk-python/{__version__}" + for request in captured + )