Skip to content

Commit af755c8

Browse files
committed
Add NodeReader for node config and update controllers
Introduced NodeReader to manage node URLs, queries, and params from nodes.json. Updated address_controller.py and transaction_controller.py to use NodeReader instead of config_manager for building API URLs. Added nodes.json for node and query configuration, and adjusted .dockerignore and .gitignore for IDE and venv exclusions.
1 parent 0621ff1 commit af755c8

File tree

9 files changed

+148
-10
lines changed

9 files changed

+148
-10
lines changed

.dockerignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Git
22
.git
33
.gitignore
4-
4+
venv/*
5+
.idea
56
# Python
67
__pycache__
78
*.pyc
@@ -13,7 +14,6 @@ venv/
1314
.env
1415

1516
# IDE
16-
.idea
1717
.vscode
1818
*.swp
1919
*.swo

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
venv

app/config/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .config_manager import config
2-
3-
__all__ = ['config']
2+
from .node_reader import NodeReader
3+
__all__ = ['config', 'NodeReader']
3.57 KB
Binary file not shown.

app/config/config_manager.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ def __init__(self):
5151

5252
config_path = os.path.join(current, 'config.json')
5353

54-
logging.info(f"Looking for config file at: {config_path}")
55-
5654
if not os.path.exists(config_path):
5755
logging.error(f"Config file not found at: {config_path}")
5856
raise FileNotFoundError(f"Config file not found at: {config_path}")

app/config/node_reader.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import json
2+
import os
3+
from typing import Dict, List, Optional, Any
4+
from dataclasses import dataclass
5+
6+
@dataclass
7+
class Node:
8+
nodes: List[str]
9+
query: Dict[str, Any]
10+
params: Dict[str, Any]
11+
12+
class NodeReader:
13+
def __init__(self) -> None:
14+
current = os.getcwd()
15+
self.path = os.path.join(current, "nodes.json")
16+
self.nodes: List[Node] = []
17+
18+
@property
19+
def data(self) -> List[Node]:
20+
return self.nodes
21+
22+
@data.setter
23+
def data(self, value: List[Node]) -> None:
24+
self.nodes = value
25+
26+
@staticmethod
27+
def reader():
28+
try:
29+
current = os.path.dirname(os.path.abspath(__file__))
30+
node_path = os.path.join(current, 'nodes.json')
31+
if not os.path.exists(node_path):
32+
raise FileNotFoundError(f"Config file not found at: {node_path}")
33+
34+
with open(node_path, 'r') as f:
35+
content = json.load(f)
36+
return content
37+
except Exception as e:
38+
print(f"Failed to NodeReader: {str(e)}")
39+
raise
40+
41+
@classmethod
42+
def get_nodes(cls) -> List[Node]:
43+
reader = cls.reader()
44+
nodes = reader.get('nodes')
45+
if not nodes:
46+
raise ValueError("No nodes found in config file")
47+
return nodes
48+
49+
@classmethod
50+
def get_query(cls, _Query: str) -> Optional[str]:
51+
"""
52+
Standard queries:
53+
- address
54+
- tx
55+
- block
56+
- blockIndex
57+
"""
58+
query_standard = ["address", "tx", "block", "blockIndex"]
59+
if _Query not in query_standard:
60+
raise ValueError(f"Invalid query type: {_Query}")
61+
reader = cls.reader()
62+
_query = reader.get('query')
63+
if not _query:
64+
raise ValueError("No nodes found in config file")
65+
return _query.get(_Query)
66+
67+
@classmethod
68+
def get_params(cls) -> Dict[str, Any]:
69+
reader = cls.reader()
70+
params = reader.get('params')
71+
if not params:
72+
raise ValueError("No nodes found in config file")
73+
return params
74+
75+
@classmethod
76+
def get_addr_params(cls, method: str) -> Optional[str]:
77+
"""params for address endpoint
78+
Standard methods:
79+
- basic # return address details without txs details
80+
- txs # return address details with txs all details
81+
- txsLight # return address details with txid and vin and vout
82+
- txLight # return address details with txid only
83+
"""
84+
params = cls.get_params()
85+
addr = params.get('addr')
86+
if not addr:
87+
raise ValueError("No address params found in config file")
88+
return addr.get(method)
89+
90+
# Example Usage:
91+
# nodereader = NodeReader()
92+
# nodes = nodereader.get_nodes()
93+
# print(nodes)
94+
# addr_query = nodereader.get_query('address')
95+
# tx_query = nodereader.get_query('tx')
96+
# block_query = nodereader.get_query('block')
97+
# block_index_query = nodereader.get_query('blockIndex')
98+
# addr_params = nodereader.get_addr_params('basic')
99+
# tx_params = nodereader.get_addr_params('txs')
100+
# tx_light_params = nodereader.get_addr_params('txsLight')

app/config/nodes.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"nodes": [
3+
"https://bitcoin.atomicwallet.io/api/v2",
4+
"https://btcbook.guarda.co/api/v2",
5+
"https://btc1.trezor.io/api/v2"
6+
],
7+
"query": {
8+
"address": "address/",
9+
"tx": "tx/",
10+
"block": "block/",
11+
"blockIndex": "block-index/"
12+
},
13+
"params": {
14+
"addr": {
15+
"basic": "?details=basic",
16+
"txs": "?details=txs",
17+
"txsLight": "?details=txslight",
18+
"txLight": "?details=txlight"
19+
}
20+
}
21+
}

app/controllers/address_controller.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
from datetime import datetime
22
from typing import Optional, Dict, Any
33
from app.config.config_manager import config
4+
from app.config.node_reader import NodeReader
45
from app.utils.api_client import make_request
56

7+
nr = NodeReader()
8+
base_urls = nr.get_nodes() # Get base urls List
9+
addr_query = nr.get_query('address')
10+
tx_query = nr.get_query('tx')
11+
block_query = nr.get_query('block')
12+
block_index_query = nr.get_query('blockIndex')
13+
params_basic = nr.get_addr_params('basic')
14+
params_txs = nr.get_addr_params('txs')
15+
params_txslight = nr.get_addr_params('txsLight')
16+
node_url = base_urls[0] # atomic = 0 | guarda = 1 | trezor = 2
17+
18+
619
def get_address_details(address: str, page: int = 1, per_page: int = 10) -> Optional[Dict[str, Any]]:
720
"""Get address information with transaction details"""
821
try:
9-
# Get address details with full transactions
10-
url = config.get_node_url('address') + address + config.get_node_suffix('address', 'txs')
22+
url = f"{node_url}{addr_query}{address}{params_basic}"
1123
addr = make_request(url)
12-
1324
if not addr:
1425
return None
1526

app/controllers/transaction_controller.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
from datetime import datetime
22
from typing import Optional, Dict, Any
33
from app.config.config_manager import config
4+
from app.config.node_reader import NodeReader
45
from app.utils.api_client import make_request
56

7+
nr = NodeReader()
8+
nodes = nr.get_nodes()
9+
tx_query = nr.get_query('tx')
10+
basic_url = f"{nodes[0]}{tx_query}"
11+
612
def get_transaction_details(tx_hash: str) -> Optional[Dict[str, Any]]:
713
"""Get detailed transaction information"""
814
try:
9-
url = config.get_node_url('tx') + tx_hash
15+
url = f"{basic_url}{tx_hash}?page=1"
1016
tx = make_request(url)
1117

1218
if not tx:

0 commit comments

Comments
 (0)