Skip to content

Commit 81a0aed

Browse files
Merge pull request #10 from Geocodio/feat/add-user-agent-header
feat: Add user-agent header to all API requests
2 parents cb1433a + bb185ae commit 81a0aed

File tree

4 files changed

+140
-5
lines changed

4 files changed

+140
-5
lines changed

src/geocodio/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
A Python client for the Geocodio API.
44
"""
55

6+
from ._version import __version__
67
from .client import GeocodioClient
78

8-
__version__ = "0.1.0"
9-
__all__ = ["GeocodioClient"]
9+
__all__ = ["GeocodioClient", "__version__"]

src/geocodio/_version.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Version information for geocodio package."""
2+
3+
__version__ = "0.1.0"

src/geocodio/client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import httpx
1313

14+
from geocodio._version import __version__
15+
1416
# Set up logger early to capture all logs
1517
logger = logging.getLogger("geocodio")
1618

@@ -30,6 +32,7 @@ class GeocodioClient:
3032
DEFAULT_SINGLE_TIMEOUT = 5.0
3133
DEFAULT_BATCH_TIMEOUT = 1800.0 # 30 minutes
3234
LIST_API_TIMEOUT = 60.0
35+
USER_AGENT = f"geocodio-library-python/{__version__}"
3336

3437
@staticmethod
3538
def get_status_exception_mappings() -> Dict[
@@ -183,8 +186,11 @@ def _request(
183186
if timeout is None:
184187
timeout = self.single_timeout
185188

186-
# Set up authorization header
187-
headers = {"Authorization": f"Bearer {self.api_key}"}
189+
# Set up authorization and user-agent headers
190+
headers = {
191+
"Authorization": f"Bearer {self.api_key}",
192+
"User-Agent": self.USER_AGENT
193+
}
188194

189195
logger.debug(f"Using timeout: {timeout}s")
190196
resp = self._http.request(method, endpoint, params=params, json=json, files=files, headers=headers, timeout=timeout)

tests/unit/test_client.py

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,130 @@ def test_geocode_with_congressional_districts(mock_request):
203203
assert len(response.results[0].fields.congressional_districts) == 1
204204
assert response.results[0].fields.congressional_districts[0].name == "Virginia's 8th congressional district"
205205
assert response.results[0].fields.congressional_districts[0].district_number == 8
206-
assert response.results[0].fields.congressional_districts[0].congress_number == "118"
206+
assert response.results[0].fields.congressional_districts[0].congress_number == "118"
207+
208+
209+
def test_user_agent_header_in_request(mocker):
210+
"""Test that the User-Agent header is included in all requests."""
211+
from geocodio import __version__
212+
213+
# Mock the httpx.Client.request method to capture headers
214+
mock_httpx_request = mocker.patch('httpx.Client.request')
215+
mock_httpx_request.return_value = httpx.Response(200, json={
216+
"input": {"address_components": {"q": "1109 N Highland St, Arlington, VA"}},
217+
"results": [{
218+
"address_components": {
219+
"number": "1109",
220+
"street": "N Highland St",
221+
"city": "Arlington",
222+
"state": "VA"
223+
},
224+
"formatted_address": "1109 N Highland St, Arlington, VA",
225+
"location": {"lat": 38.886665, "lng": -77.094733},
226+
"accuracy": 1.0,
227+
"accuracy_type": "rooftop",
228+
"source": "Virginia GIS Clearinghouse"
229+
}]
230+
})
231+
232+
client = GeocodioClient("test-api-key")
233+
client.geocode("1109 N Highland St, Arlington, VA")
234+
235+
# Verify request was made with correct headers
236+
mock_httpx_request.assert_called_once()
237+
call_args = mock_httpx_request.call_args
238+
headers = call_args.kwargs.get('headers', {})
239+
240+
assert 'User-Agent' in headers
241+
assert headers['User-Agent'] == f"geocodio-library-python/{__version__}"
242+
assert 'Authorization' in headers
243+
assert headers['Authorization'] == "Bearer test-api-key"
244+
245+
246+
def test_user_agent_header_format():
247+
"""Test that the User-Agent header has the correct format."""
248+
from geocodio import __version__
249+
250+
client = GeocodioClient("test-api-key")
251+
expected_user_agent = f"geocodio-library-python/{__version__}"
252+
assert client.USER_AGENT == expected_user_agent
253+
254+
255+
def test_user_agent_header_in_batch_request(mocker):
256+
"""Test that the User-Agent header is included in batch requests."""
257+
from geocodio import __version__
258+
259+
# Mock the httpx.Client.request method
260+
mock_httpx_request = mocker.patch('httpx.Client.request')
261+
mock_httpx_request.return_value = httpx.Response(200, json={
262+
"results": []
263+
})
264+
265+
client = GeocodioClient("test-api-key")
266+
client.geocode(["Address 1", "Address 2"])
267+
268+
# Verify headers in batch request
269+
mock_httpx_request.assert_called_once()
270+
call_args = mock_httpx_request.call_args
271+
headers = call_args.kwargs.get('headers', {})
272+
273+
assert headers['User-Agent'] == f"geocodio-library-python/{__version__}"
274+
275+
276+
def test_user_agent_header_in_reverse_geocode(mocker):
277+
"""Test that the User-Agent header is included in reverse geocoding requests."""
278+
from geocodio import __version__
279+
280+
# Mock the httpx.Client.request method
281+
mock_httpx_request = mocker.patch('httpx.Client.request')
282+
mock_httpx_request.return_value = httpx.Response(200, json={
283+
"results": [{
284+
"address_components": {
285+
"number": "1109",
286+
"street": "N Highland St",
287+
"city": "Arlington",
288+
"state": "VA"
289+
},
290+
"formatted_address": "1109 N Highland St, Arlington, VA",
291+
"location": {"lat": 38.886665, "lng": -77.094733},
292+
"accuracy": 1.0,
293+
"accuracy_type": "rooftop",
294+
"source": "Virginia GIS Clearinghouse"
295+
}]
296+
})
297+
298+
client = GeocodioClient("test-api-key")
299+
client.reverse("38.886665,-77.094733")
300+
301+
# Verify headers in reverse geocode request
302+
mock_httpx_request.assert_called_once()
303+
call_args = mock_httpx_request.call_args
304+
headers = call_args.kwargs.get('headers', {})
305+
306+
assert headers['User-Agent'] == f"geocodio-library-python/{__version__}"
307+
308+
309+
def test_user_agent_header_in_list_api(mocker):
310+
"""Test that the User-Agent header is included in List API requests."""
311+
from geocodio import __version__
312+
313+
# Mock the httpx.Client.request method
314+
mock_httpx_request = mocker.patch('httpx.Client.request')
315+
mock_httpx_request.return_value = httpx.Response(200, json={
316+
"data": [],
317+
"current_page": 1,
318+
"from": 0,
319+
"to": 0,
320+
"path": "",
321+
"per_page": 10
322+
})
323+
324+
client = GeocodioClient("test-api-key")
325+
client.get_lists()
326+
327+
# Verify headers in list API request
328+
mock_httpx_request.assert_called_once()
329+
call_args = mock_httpx_request.call_args
330+
headers = call_args.kwargs.get('headers', {})
331+
332+
assert headers['User-Agent'] == f"geocodio-library-python/{__version__}"

0 commit comments

Comments
 (0)