Skip to content

Commit ead74c6

Browse files
committed
ADD: Custom gateway handling to HistoricalClient
1 parent 09fc648 commit ead74c6

File tree

5 files changed

+111
-7
lines changed

5 files changed

+111
-7
lines changed

databento/common/enums.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def __str__(self) -> str:
102102
class HistoricalGateway(StringyMixin, str, Enum):
103103
"""Represents a historical data center gateway location."""
104104

105-
BO1 = "bo1"
105+
BO1 = "https://hist.databento.com"
106106

107107

108108
@unique

databento/common/validation.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from enum import Enum
22
from typing import Optional, Type, TypeVar, Union
3+
from urllib.parse import urlsplit, urlunsplit
34

45

56
E = TypeVar("E", bound=Enum)
@@ -76,3 +77,37 @@ def validate_maybe_enum(
7677
if value is None:
7778
return None
7879
return validate_enum(value, enum, param)
80+
81+
82+
def validate_gateway(
83+
url: str,
84+
) -> str:
85+
"""
86+
Validate that the given value is a valid gateway URL.
87+
88+
Parameters
89+
----------
90+
url : str
91+
The URL to validate
92+
93+
Returns
94+
-------
95+
str
96+
The gateway URL if it is valid.
97+
98+
Raises
99+
------
100+
ValueError
101+
If the URL is invalid.
102+
103+
"""
104+
url_chunks = urlsplit(url)
105+
106+
if not any([url_chunks.netloc, url_chunks.path]):
107+
raise ValueError(f"`{url}` is not a valid URL")
108+
109+
if url_chunks.netloc:
110+
return urlunsplit(
111+
components=("https", url_chunks.netloc, url_chunks.path, "", ""),
112+
)
113+
return urlunsplit(components=("https", url_chunks.path, "", "", ""))

databento/historical/client.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from databento.common.enums import HistoricalGateway
55
from databento.common.logging import log_info
6+
from databento.common.validation import validate_gateway
67
from databento.historical.api.batch import BatchHttpAPI
78
from databento.historical.api.metadata import MetadataHttpAPI
89
from databento.historical.api.symbology import SymbologyHttpAPI
@@ -39,10 +40,10 @@ def __init__(
3940
if key is None or not isinstance(key, str) or key.isspace():
4041
raise ValueError(f"invalid API key, was {key}")
4142

42-
# Configure data access gateway
43-
gateway = str(gateway)
44-
if gateway == "bo1":
45-
gateway = "https://hist.databento.com"
43+
try:
44+
gateway = HistoricalGateway(gateway)
45+
except ValueError:
46+
gateway = validate_gateway(str(gateway))
4647

4748
self._key = key
4849
self._gateway = gateway

tests/test_common_validation.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
import pytest
55
from databento.common.enums import Encoding
6-
from databento.common.validation import validate_enum, validate_maybe_enum
6+
from databento.common.validation import (
7+
validate_enum,
8+
validate_gateway,
9+
validate_maybe_enum,
10+
)
711

812

913
class TestValidation:
@@ -47,3 +51,28 @@ def test_validate_enum_given_valid_value_returns_expected_output(
4751
def test_validate_maybe_enum_give_none_returns_none(self) -> None:
4852
# Arrange, Act, Assert
4953
assert validate_maybe_enum(None, Encoding, "encoding") is None
54+
55+
@pytest.mark.parametrize(
56+
"url, expected",
57+
[
58+
pytest.param("databento.com", "https://databento.com"),
59+
pytest.param("hist.databento.com", "https://hist.databento.com"),
60+
pytest.param("http://databento.com", "https://databento.com"),
61+
pytest.param("http://hist.databento.com", "https://hist.databento.com"),
62+
pytest.param("//", ValueError),
63+
pytest.param("", ValueError),
64+
],
65+
)
66+
def test_validate_gateway(
67+
self,
68+
url: str,
69+
expected: Union[str, Type[Exception]],
70+
) -> None:
71+
"""
72+
Tests several correct and malformed URLs.
73+
"""
74+
if isinstance(expected, str):
75+
assert validate_gateway(url) == expected
76+
else:
77+
with pytest.raises(expected):
78+
validate_gateway(url)

tests/test_historical_client.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,46 @@ def test_custom_gateway_returns_expected(self) -> None:
5555
client = db.Historical(key="DUMMY_API_KEY", gateway=ny4_gateway)
5656

5757
# Assert
58-
assert client.gateway == ny4_gateway
58+
assert client.gateway == "https://ny4.databento.com"
59+
60+
@pytest.mark.parametrize(
61+
"gateway",
62+
[
63+
"//",
64+
"",
65+
],
66+
)
67+
def test_custom_gateway_error(
68+
self,
69+
gateway: str,
70+
) -> None:
71+
"""
72+
Test that setting a custom gateway to an invalid url raises an exception.
73+
"""
74+
# Arrange, Act, Assert
75+
with pytest.raises(ValueError):
76+
db.Historical(key="DUMMY_API_KEY", gateway=gateway)
77+
78+
@pytest.mark.parametrize(
79+
"gateway, expected",
80+
[
81+
["hist.databento.com", "https://hist.databento.com"],
82+
["http://hist.databento.com", "https://hist.databento.com"],
83+
],
84+
)
85+
def test_custom_gateway_force_https(
86+
self,
87+
gateway: str,
88+
expected: str,
89+
) -> None:
90+
"""
91+
Test that custom gateways are forced to the https scheme.
92+
"""
93+
# Arrange Act
94+
client = db.Historical(key="DUMMY_API_KEY", gateway=gateway)
95+
96+
# Assert
97+
assert client.gateway == expected
5998

6099
@pytest.mark.skipif(sys.version_info < (3, 8), reason="incompatible mocking")
61100
def test_re_request_symbology_makes_expected_request(

0 commit comments

Comments
 (0)