diff --git a/CHANGELOG.md b/CHANGELOG.md index 21bbaca6..d6f16f42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ All notable changes to this project will be documented in this file. -## [1.2.0] - 2024-02-12 +## [1.3.1] - 2024-02-29 +### Changed +- Updated cookie assistant logic to support the Indexer exchange server not using cookies and the chain server using them + +## [1.3.0] - 2024-02-12 ### Changed - Removed `asyncio` from the dependencies diff --git a/pyinjective/core/network.py b/pyinjective/core/network.py index c739151e..93d006eb 100644 --- a/pyinjective/core/network.py +++ b/pyinjective/core/network.py @@ -23,7 +23,10 @@ async def chain_metadata(self, metadata_query_provider: Callable) -> Tuple[Tuple async def exchange_metadata(self, metadata_query_provider: Callable) -> Tuple[Tuple[str, str]]: cookie = await self.exchange_cookie(metadata_query_provider=metadata_query_provider) - return (("cookie", cookie),) + metadata = None + if cookie is not None and cookie != "": + metadata = (("cookie", cookie),) + return metadata class KubernetesLoadBalancedCookieAssistant(CookieAssistant): @@ -67,7 +70,7 @@ async def _fetch_exchange_cookie(self, metadata_query_provider: Callable): cookie_info = next((value for key, value in metadata if key == "set-cookie"), None) if cookie_info is None: - raise RuntimeError(f"Error fetching exchange cookie ({metadata})") + cookie_info = "" self._exchange_cookie = cookie_info @@ -83,7 +86,11 @@ def _is_cookie_expired(self, cookie_data: str) -> bool: cookie = SimpleCookie() cookie.load(cookie_data) - expiration_time = datetime.datetime.strptime(cookie["GCLB"]["expires"], "%a, %d-%b-%Y %H:%M:%S %Z").timestamp() + expiration_data: Optional[str] = cookie.get("GCLB", {}).get("expires", None) + if expiration_data is None: + expiration_time = 0 + else: + expiration_time = datetime.datetime.strptime(expiration_data, "%a, %d-%b-%Y %H:%M:%S %Z").timestamp() timestamp_diff = expiration_time - time.time() return timestamp_diff < self.SESSION_RENEWAL_OFFSET @@ -130,7 +137,7 @@ async def _fetch_exchange_cookie(self, metadata_query_provider: Callable): cookie_info = next((value for key, value in metadata if key == "set-cookie"), None) if cookie_info is None: - raise RuntimeError(f"Error fetching exchange cookie ({metadata})") + cookie_info = "" self._exchange_cookie = cookie_info diff --git a/pyproject.toml b/pyproject.toml index e53facef..81c1888f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "injective-py" -version = "1.3.0" +version = "1.3.1" description = "Injective Python SDK, with Exchange API Client" authors = ["Injective Labs "] license = "Apache-2.0" diff --git a/tests/core/test_network.py b/tests/core/test_network.py new file mode 100644 index 00000000..479f7027 --- /dev/null +++ b/tests/core/test_network.py @@ -0,0 +1,105 @@ +import pytest + +from pyinjective.core.network import BareMetalLoadBalancedCookieAssistant, KubernetesLoadBalancedCookieAssistant + + +class TestBareMetalLoadBalancedCookieAssistant: + @pytest.mark.asyncio + async def test_chain_metadata(self): + assistant = BareMetalLoadBalancedCookieAssistant() + dummy_metadata = [("set-cookie", "expected_cookie")] + + async def dummy_metadata_provider(): + return dummy_metadata + + metadata = await assistant.chain_metadata(metadata_query_provider=dummy_metadata_provider) + expected_metadata = (("cookie", "expected_cookie"),) + + assert expected_metadata == metadata + + @pytest.mark.asyncio + async def test_chain_metadata_fails_when_cookie_info_not_included_in_server_response(self): + assistant = BareMetalLoadBalancedCookieAssistant() + dummy_metadata = [("invalid_key", "invalid_value")] + + async def dummy_metadata_provider(): + return dummy_metadata + + with pytest.raises(RuntimeError, match=f"Error fetching chain cookie ({dummy_metadata})"): + await assistant.chain_metadata(metadata_query_provider=dummy_metadata_provider) + + @pytest.mark.asyncio + async def test_exchange_metadata(self): + assistant = BareMetalLoadBalancedCookieAssistant() + dummy_metadata = [("set-cookie", "expected_cookie")] + + async def dummy_metadata_provider(): + return dummy_metadata + + metadata = await assistant.exchange_metadata(metadata_query_provider=dummy_metadata_provider) + expected_metadata = (("cookie", "expected_cookie"),) + + assert expected_metadata == metadata + + @pytest.mark.asyncio + async def test_exchange_metadata_is_none_when_cookie_info_not_included_in_server_response(self): + assistant = BareMetalLoadBalancedCookieAssistant() + dummy_metadata = [("invalid_key", "invalid_value")] + + async def dummy_metadata_provider(): + return dummy_metadata + + metadata = await assistant.exchange_metadata(metadata_query_provider=dummy_metadata_provider) + + assert metadata is None + + +class TestKubernetesLoadBalancedCookieAssistant: + @pytest.mark.asyncio + async def test_chain_metadata(self): + assistant = KubernetesLoadBalancedCookieAssistant() + dummy_metadata = [("set-cookie", "expected_cookie")] + + async def dummy_metadata_provider(): + return dummy_metadata + + metadata = await assistant.chain_metadata(metadata_query_provider=dummy_metadata_provider) + expected_metadata = (("cookie", "expected_cookie"),) + + assert expected_metadata == metadata + + @pytest.mark.asyncio + async def test_chain_metadata_fails_when_cookie_info_not_included_in_server_response(self): + assistant = KubernetesLoadBalancedCookieAssistant() + dummy_metadata = [("invalid_key", "invalid_value")] + + async def dummy_metadata_provider(): + return dummy_metadata + + with pytest.raises(RuntimeError, match=f"Error fetching chain cookie ({dummy_metadata})"): + await assistant.chain_metadata(metadata_query_provider=dummy_metadata_provider) + + @pytest.mark.asyncio + async def test_exchange_metadata(self): + assistant = KubernetesLoadBalancedCookieAssistant() + dummy_metadata = [("set-cookie", "expected_cookie")] + + async def dummy_metadata_provider(): + return dummy_metadata + + metadata = await assistant.exchange_metadata(metadata_query_provider=dummy_metadata_provider) + expected_metadata = (("cookie", "expected_cookie"),) + + assert expected_metadata == metadata + + @pytest.mark.asyncio + async def test_exchange_metadata_is_none_when_cookie_info_not_included_in_server_response(self): + assistant = KubernetesLoadBalancedCookieAssistant() + dummy_metadata = [("invalid_key", "invalid_value")] + + async def dummy_metadata_provider(): + return dummy_metadata + + metadata = await assistant.exchange_metadata(metadata_query_provider=dummy_metadata_provider) + + assert metadata is None