• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-eth-py / 15611201093

12 Jun 2025 12:59PM UTC coverage: 93.976%. First build
15611201093

push

github

web-flow
Add api key to Safe services API and update base URLs (#1752)

* Add api key to safe services API and update base URLs

* Apply PR suggestions

47 of 50 new or added lines in 4 files covered. (94.0%)

9609 of 10225 relevant lines covered (93.98%)

2.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.05
/safe_eth/safe/api/base_api.py
1
from abc import ABC, abstractmethod
3✔
2
from typing import Dict, Optional
3✔
3

4
import requests
3✔
5

6
from safe_eth.eth.ethereum_client import (
3✔
7
    EthereumClient,
8
    EthereumNetwork,
9
    EthereumNetworkNotSupported,
10
)
11
from safe_eth.util.http import build_full_url, prepare_http_session
3✔
12

13

14
class SafeAPIException(Exception):
3✔
15
    pass
3✔
16

17

18
class SafeBaseAPI(ABC):
3✔
19
    def __init__(
3✔
20
        self,
21
        network: EthereumNetwork,
22
        ethereum_client: Optional[EthereumClient] = None,
23
        base_url: Optional[str] = None,
24
        api_key: Optional[str] = None,
25
        request_timeout: int = 10,
26
    ):
27
        """
28
        :param network: Network for the transaction service
29
        :param ethereum_client:
30
        :param base_url: If a custom transaction service is used
31
        :param api_key: Api key of authenticated Safe services
32
        :raises: EthereumNetworkNotSupported
33
        """
34
        self.network = network
3✔
35
        self.ethereum_client = ethereum_client
3✔
36
        self.base_url = self._get_api_base_url(network, base_url)
3✔
37
        self.api_key = api_key
3✔
38
        self.http_session = prepare_http_session(10, 100)
3✔
39
        self.request_timeout = request_timeout
3✔
40

41
    @abstractmethod
3✔
42
    def _get_url_by_network(self, network: EthereumNetwork) -> Optional[str]:
3✔
43
        """
44
        Should return the base URL for the given network.
45
        :param network: EthereumNetwork to get the base URL for.
46
        :return: base URL for the given network. None if not found.
47
        """
NEW
48
        pass
×
49

50
    def _get_api_base_url(
3✔
51
        self, network: EthereumNetwork, custom_base_url: Optional[str] = None
52
    ) -> str:
53
        """
54
        Returns the base API URL for the specified Ethereum network.
55
        If a custom_base_url is provided, it will be used instead of the default.
56
        Otherwise, the method attempts to retrieve the URL associated with the given network.
57

58
        :param network: The EthereumNetwork to get the base URL for.
59
        :param custom_base_url: Optional custom base URL to override the default.
60
        :return: The base URL corresponding to the specified network.
61
        :raises EthereumNetworkNotSupported: If the network is not supported and no URL is found.
62
        """
63
        base_url = custom_base_url or self._get_url_by_network(network)
3✔
64
        if not base_url:
3✔
65
            raise EthereumNetworkNotSupported(network)
3✔
66
        return base_url
3✔
67

68
    @classmethod
3✔
69
    def from_ethereum_client(cls, ethereum_client: EthereumClient) -> "SafeBaseAPI":
3✔
70
        ethereum_network = ethereum_client.get_network()
3✔
71
        return cls(ethereum_network, ethereum_client=ethereum_client)
3✔
72

73
    def _get_request_headers(self, include_json_body: bool = False) -> Dict[str, str]:
3✔
74
        """
75
        Build the default HTTP headers for a request.
76

77
        :param include_json_body: If it includes the JSON Content-Type header.
78
        :return: Dictionary of headers to include in the request.
79
        """
80
        headers = {}
3✔
81
        if include_json_body:
3✔
NEW
82
            headers["Content-Type"] = "application/json"
×
83
        if self.api_key:
3✔
84
            headers["Authorization"] = f"Bearer {self.api_key}"
3✔
85
        return headers
3✔
86

87
    def _get_request(self, url: str) -> requests.Response:
3✔
88
        full_url = build_full_url(self.base_url, url)
3✔
89
        return self.http_session.get(
3✔
90
            full_url, headers=self._get_request_headers(), timeout=self.request_timeout
91
        )
92

93
    def _post_request(self, url: str, payload: Dict) -> requests.Response:
3✔
94
        full_url = build_full_url(self.base_url, url)
×
95
        return self.http_session.post(
×
96
            full_url,
97
            json=payload,
98
            headers=self._get_request_headers(include_json_body=True),
99
            timeout=self.request_timeout,
100
        )
101

102
    def _delete_request(self, url: str, payload: Dict) -> requests.Response:
3✔
103
        full_url = build_full_url(self.base_url, url)
×
104
        return self.http_session.delete(
×
105
            full_url,
106
            json=payload,
107
            headers=self._get_request_headers(include_json_body=True),
108
            timeout=self.request_timeout,
109
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc