diff --git a/pyproject.toml b/pyproject.toml index 022fa8f..af0e4f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mospy-wallet" -version = "0.5.3" +version = "0.5.4" description = "This package is a fork of cosmospy and is a light framework for the cosmos ecosystem" authors = [ { name = "ctrl-felix", email = "dev@ctrl-felix.de" }, diff --git a/src/mospy/clients/HTTPClient.py b/src/mospy/clients/HTTPClient.py index 4fc69a4..6716ea6 100644 --- a/src/mospy/clients/HTTPClient.py +++ b/src/mospy/clients/HTTPClient.py @@ -6,7 +6,7 @@ from mospy.Transaction import Transaction from mospy.exceptions.clients import NodeException -from mospy.exceptions.clients import NodeTimeoutException, TransactionNotFound, TransactionTimeout +from mospy.exceptions.clients import NodeTimeoutException, TransactionNotFound, TransactionTimeout, NotAnApiNode class HTTPClient: @@ -15,10 +15,33 @@ class HTTPClient: Args: api (str): URL to a Api node + check_api (bool): Check if the Api node is reachable. Returns either NotAnApiNode or NodeException if the node is not healthy. """ - def __init__(self, *, api: str = "https://rest.cosmos.directory/cosmoshub"): - self._api = api + def __init__(self, *, api: str = "https://rest.cosmos.directory/cosmoshub", check_api: bool = False): + self._api = api.rstrip("/") + + # Check API if reachable + if check_api: + self.check_api() + + def check_api(self): + """ + Checks if the API is reachable. Returns a NodeException or a NotAnApiNode Exception when the check failed. + """ + try: + check_response = httpx.get(self._api + "/node_info") + except httpx.HTTPError: + raise NodeException( + "The passed url is not reachable. To disable the health check pass check_api=false to the HTTPClient constructor.") + + if check_response.status_code != 200: + # Check if the returned text matches the one returned by an RPC + if "404 page not found" in check_response.text: + raise NotAnApiNode("Please pass an API endpoint to the HTTPClient. You probably passed an RPC.") + else: + raise NodeException( + "Health couldn't be verified. To disable the health check pass check_api=false to the HTTPClient constructor.") def _make_post_request(self, path, payload, timeout): try: diff --git a/src/mospy/exceptions/clients.py b/src/mospy/exceptions/clients.py index 644e32b..d2c45a2 100644 --- a/src/mospy/exceptions/clients.py +++ b/src/mospy/exceptions/clients.py @@ -12,4 +12,7 @@ class TransactionTimeout(Exception): class TransactionNotFound(Exception): """Raised when the transaction couldn't be found on chain.""" - pass \ No newline at end of file + pass + +class NotAnApiNode(Exception): + """The url passed to the constructor couldn't be detected as API node. This might be because you passed the rpc endpoint instead.""" \ No newline at end of file diff --git a/tests/clients/test_httpclient.py b/tests/clients/test_httpclient.py index fe97818..dd0abcd 100644 --- a/tests/clients/test_httpclient.py +++ b/tests/clients/test_httpclient.py @@ -5,6 +5,8 @@ from mospy import Transaction from mospy.clients import HTTPClient +from mospy.exceptions.clients import NotAnApiNode, NodeException + API = "https://cosmos-rest.publicnode.com" class TestHTTPClientClass: @@ -21,6 +23,23 @@ def test_account_data_loading(self): assert account.next_sequence >= 0 + def test_node_health_check(self): + try: + client = HTTPClient(api="https://cosmos-rpc.publicnode.com", check_api=True) + except NotAnApiNode: + assert True + else: + assert False + + try: + client = HTTPClient(api="https://dead-url-awdbnawdbauiowd.com", check_api=True) + except NodeException: + assert True + else: + assert False + + client = HTTPClient(api="https://dead-url-awdbnawdbauiowd.com", check_api=False) + def test_transaction_submitting(self): account = Account( seed_phrase=self.seed_phrase,