Skip to content

Commit

Permalink
feat: method to adjustable api endpoint (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbarnsley authored Aug 21, 2024
1 parent a6ddced commit 7f3f398
Show file tree
Hide file tree
Showing 26 changed files with 281 additions and 220 deletions.
2 changes: 1 addition & 1 deletion client/api/api_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
class ApiNodes(Resource):

def all(self, **kwargs):
return self.request_get('api-nodes', kwargs)
return self.with_endpoint('api').request_get('api-nodes', kwargs)
2 changes: 1 addition & 1 deletion client/api/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
class Blockchain(Resource):

def blockchain(self):
return self.request_get('blockchain')
return self.with_endpoint('api').request_get('blockchain')
8 changes: 4 additions & 4 deletions client/api/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ def all(self, page=None, limit=100, **kwargs):
return self.request_get('blocks', params)

def get(self, block_id):
return self.request_get('blocks/{}'.format(block_id))
return self.with_endpoint('api').request_get(f'blocks/{block_id}')

def first(self):
return self.request_get('blocks/first')
return self.with_endpoint('api').request_get('blocks/first')

def last(self):
return self.request_get('blocks/last')
return self.with_endpoint('api').request_get('blocks/last')

def transactions(self, block_id, page=None, limit=100):
params = {
'page': page,
'limit': limit,
}
return self.request_get('blocks/{}/transactions'.format(block_id), params)
return self.with_endpoint('api').request_get(f'blocks/{block_id}/transactions', params)
2 changes: 1 addition & 1 deletion client/api/commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
class Commits(Resource):

def show(self, height):
return self.request_get(f'commits/{height}')
return self.with_endpoint('api').request_get(f'commits/{height}')
8 changes: 4 additions & 4 deletions client/api/delegates.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ def all(self, page=None, limit=100, **kwargs):
'limit': limit,
**extra_params
}
return self.request_get('delegates', params)
return self.with_endpoint('api').request_get('delegates', params)

def get(self, delegate_id):
return self.request_get('delegates/{}'.format(delegate_id))
return self.with_endpoint('api').request_get(f'delegates/{delegate_id}')

def blocks(self, delegate_id, page=None, limit=100):
params = {
'page': page,
'limit': limit,
}
return self.request_get('delegates/{}/blocks'.format(delegate_id), params)
return self.with_endpoint('api').request_get(f'delegates/{delegate_id}/blocks', params)

def voters(self, delegate_id, page=None, limit=100):
params = {
'page': page,
'limit': limit,
}
return self.request_get('delegates/{}/voters'.format(delegate_id), params)
return self.with_endpoint('api').request_get(f'delegates/{delegate_id}/voters', params)
10 changes: 5 additions & 5 deletions client/api/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
class Node(Resource):

def status(self):
return self.request_get('node/status')
return self.with_endpoint('api').request_get('node/status')

def syncing(self):
return self.request_get('node/syncing')
return self.with_endpoint('api').request_get('node/syncing')

def configuration(self):
return self.request_get('node/configuration')
return self.with_endpoint('api').request_get('node/configuration')

def crypto(self):
return self.request_get('node/configuration/crypto')
return self.with_endpoint('api').request_get('node/configuration/crypto')

def fees(self, days=None):
params = {
'days': days,
}
return self.request_get('node/fees', params)
return self.with_endpoint('api').request_get('node/fees', params)
4 changes: 2 additions & 2 deletions client/api/peers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def all(self, os=None, status=None, port=None, version=None, order_by=None,
'page': page,
'limit': limit,
}
return self.request_get('peers', params)
return self.with_endpoint('api').request_get('peers', params)

def get(self, ip):
return self.request_get('peers/{}'.format(ip))
return self.with_endpoint('api').request_get(f'peers/{ip}')
6 changes: 3 additions & 3 deletions client/api/rounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
class Rounds(Resource):

def all(self, **kwargs):
return self.request_get('rounds', kwargs)
return self.with_endpoint('api').request_get('rounds', kwargs)

def show(self, round_id):
return self.request_get(f'rounds/{round_id}')
return self.with_endpoint('api').request_get(f'rounds/{round_id}')

def delegates(self, round_id):
return self.request_get(f'rounds/{round_id}/delegates')
return self.with_endpoint('api').request_get(f'rounds/{round_id}/delegates')
16 changes: 8 additions & 8 deletions client/api/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ def all(self, page=None, limit=100, **kwargs):
'limit': limit,
**extra_params
}
return self.request_get('transactions', params)
return self.with_endpoint('api').request_get('transactions', params)

def create(self, transactions):
return self.request_post('transactions', data={'transactions': transactions})
return self.with_endpoint('transactions').request_post('transactions', data={'transactions': transactions})

def get(self, transaction_id):
return self.request_get('transactions/{}'.format(transaction_id))
return self.with_endpoint('api').request_get(f'transactions/{transaction_id}')

def all_unconfirmed(self, limit=100, offset=None, **kwargs):
extra_params = {name: kwargs[name] for name in kwargs if kwargs[name] is not None}
Expand All @@ -25,16 +25,16 @@ def all_unconfirmed(self, limit=100, offset=None, **kwargs):
'offset': offset,
**extra_params
}
return self.request_get('transactions/unconfirmed', params)
return self.with_endpoint('api').request_get('transactions/unconfirmed', params)

def get_unconfirmed(self, transaction_id):
return self.request_get('transactions/unconfirmed/{}'.format(transaction_id))
return self.with_endpoint('api').request_get(f'transactions/unconfirmed/{transaction_id}')

def types(self):
return self.request_get('transactions/types')
return self.with_endpoint('api').request_get('transactions/types')

def fees(self):
return self.request_get('transactions/fees')
return self.with_endpoint('api').request_get('transactions/fees')

def schemas(self):
return self.request_get('transactions/schemas')
return self.with_endpoint('api').request_get('transactions/schemas')
2 changes: 1 addition & 1 deletion client/api/votes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ def all(self, page=None, limit=100):
return self.request_get('votes', params)

def get(self, vote_id):
return self.request_get('votes/{}'.format(vote_id))
return self.with_endpoint('api').request_get(f'votes/{vote_id}')
14 changes: 7 additions & 7 deletions client/api/wallets.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ def all(self, page=None, limit=100):
'page': page,
'limit': limit,
}
return self.request_get('wallets', params)
return self.with_endpoint('api').request_get('wallets', params)

def top(self, page=None, limit=100):
params = {
'page': page,
'limit': limit
}
return self.request_get('wallets/top', params)
return self.with_endpoint('api').request_get('wallets/top', params)

def get(self, wallet_id):
return self.request_get('wallets/{}'.format(wallet_id))
return self.with_endpoint('api').request_get(f'wallets/{wallet_id}')

def transactions(self, wallet_id, page=None, limit=100, **kwargs):
extra_params = {name: kwargs[name] for name in kwargs if kwargs[name] is not None}
Expand All @@ -27,25 +27,25 @@ def transactions(self, wallet_id, page=None, limit=100, **kwargs):
'limit': limit,
**extra_params
}
return self.request_get('wallets/{}/transactions'.format(wallet_id), params)
return self.with_endpoint('api').request_get(f'wallets/{wallet_id}/transactions', params)

def transactions_sent(self, wallet_id, page=None, limit=100):
params = {
'page': page,
'limit': limit,
}
return self.request_get('wallets/{}/transactions/sent'.format(wallet_id), params)
return self.with_endpoint('api').request_get(f'wallets/{wallet_id}/transactions/sent', params)

def transactions_received(self, wallet_id, page=None, limit=100):
params = {
'page': page,
'limit': limit,
}
return self.request_get('wallets/{}/transactions/received'.format(wallet_id), params)
return self.with_endpoint('api').request_get(f'wallets/{wallet_id}/transactions/received', params)

def votes(self, wallet_id, page=None, limit=100):
params = {
'page': page,
'limit': limit,
}
return self.request_get('wallets/{}/votes'.format(wallet_id), params)
return self.with_endpoint('api').request_get(f'wallets/{wallet_id}/votes', params)
11 changes: 5 additions & 6 deletions client/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from client.connection import Connection
from typing import Union
from client.connection import ClientHosts, Connection
from client.api.api_nodes import ApiNodes
from client.api.blockchain import Blockchain
from client.api.blocks import Blocks
Expand All @@ -13,13 +14,11 @@

class ArkClient(object):

def __init__(self, hostname):
def __init__(self, hosts: Union[str, ClientHosts]):
"""
:param string hostname: Node hostname. Examples: `http://127.0.0.1:4002` or
`http://my.domain.io/api/`. This is to allow people to server the api
on whatever url they want.
:param string hosts: hosts of the node
"""
self.connection = Connection(hostname)
self.connection = Connection(hosts)

@property
def api_nodes(self):
Expand Down
42 changes: 35 additions & 7 deletions client/connection.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@

from typing import Literal, Optional, TypedDict, Union
import backoff
import requests

from client.exceptions import ArkHTTPException, ArkParameterException
from client.exceptions import ArkHTTPException


def giveup_handler(_):
Expand All @@ -16,6 +17,11 @@ def giveup_handler(_):
on_giveup=giveup_handler)


class ClientHosts(TypedDict):
api: str
transactions: Optional[str]
evm: Optional[str]

class Session(requests.Session):

def __init__(self, *args, **kwargs):
Expand All @@ -32,21 +38,41 @@ def send(self, request, **kwargs):

def prepare_request(self, request):
if self.hostname is not None:
request.url = '{}/{}'.format(self.hostname, request.url)
request.url = f'{self.hostname}/{request.url}'
return super().prepare_request(request)


class Connection(object):
session: Session
hosts: ClientHosts

def __init__(self, hosts: Union[str, ClientHosts]):
if isinstance(hosts, str):
hosts = {
'api': hosts,
'transactions': None,
'evm': None,
}

self.hosts = hosts

def __init__(self, hostname):
self.hostname = hostname
self.session = Session(hostname=self.hostname)
# Ensure we have a session
self.with_endpoint('api')

def with_endpoint(self, endpoint: Literal['api', 'transactions', 'evm']):
"""
:param string endpoint: endpoint name
"""
host = self.hosts[endpoint]
if host is None:
host = self.hosts['api']

self.session = Session(hostname=f'{host}')

self.session.headers.update({
'Content-Type': 'application/json',
})

def _handle_response(self, response):
def _handle_response(self, response: requests.Response):
if not response.content:
raise ArkHTTPException('No content in response', response=response)

Expand All @@ -66,8 +92,10 @@ def _handle_response(self, response):

def get(self, path, params=None):
response = self.session.get(path, params=params)

return self._handle_response(response)

def post(self, path, data=None, params=None):
response = self.session.post(path, json=data, params=params)

return self._handle_response(response)
9 changes: 8 additions & 1 deletion client/resource.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
from typing import Literal
from flatten_dict import flatten

from client.connection import Connection

class Resource(object):

def __init__(self, connection):
def __init__(self, connection: Connection):
self.connection = connection

def with_endpoint(self, endpoint: Literal['api', 'transactions', 'evm']):
self.connection.with_endpoint(endpoint)
return self

def request_get(self, path, params=None):
if params:
params = flatten(params, reducer='dot')
Expand Down
12 changes: 6 additions & 6 deletions tests/api/test_api_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@
def test_api_nodes_calls_correct_url():
responses.add(
responses.GET,
'http://127.0.0.1:4002/api-nodes',
'http://127.0.0.1:4002/api/api-nodes',
json={'success': True},
status=200
)

client = ArkClient('http://127.0.0.1:4002')
client = ArkClient('http://127.0.0.1:4002/api')
client.api_nodes.all()
assert len(responses.calls) == 1
assert responses.calls[0].request.url == 'http://127.0.0.1:4002/api-nodes'
assert responses.calls[0].request.url == 'http://127.0.0.1:4002/api/api-nodes'


def test_api_nodes_calls_correct_url_with_params():
responses.add(
responses.GET,
'http://127.0.0.1:4002/api-nodes',
'http://127.0.0.1:4002/api/api-nodes',
json={'success': True},
status=200
)

client = ArkClient('http://127.0.0.1:4002')
client = ArkClient('http://127.0.0.1:4002/api')
client.api_nodes.all(query_param1='value1', query_param2='value2')
assert len(responses.calls) == 1
assert responses.calls[0].request.url.startswith('http://127.0.0.1:4002/api-nodes?')
assert responses.calls[0].request.url.startswith('http://127.0.0.1:4002/api/api-nodes?')
assert 'query_param1=value1' in responses.calls[0].request.url
assert 'query_param2=value2' in responses.calls[0].request.url
6 changes: 3 additions & 3 deletions tests/api/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
def test_blockchain_calls_correct_url():
responses.add(
responses.GET,
'http://127.0.0.1:4002/blockchain',
'http://127.0.0.1:4002/api/blockchain',
json={'success': True},
status=200
)

client = ArkClient('http://127.0.0.1:4002')
client = ArkClient('http://127.0.0.1:4002/api')
client.blockchain.blockchain()
assert len(responses.calls) == 1
assert responses.calls[0].request.url == 'http://127.0.0.1:4002/blockchain'
assert responses.calls[0].request.url == 'http://127.0.0.1:4002/api/blockchain'
Loading

0 comments on commit 7f3f398

Please sign in to comment.