Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lightapi/python] task: add NemConnector endpoints #464

Open
wants to merge 26 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
875b809
[lightapi/python] task: add BasicConnector support POST
AnthonyLaw Jun 28, 2023
027f661
[lightapi/python] task: add NemConnector for POST local/chain/blocks-…
AnthonyLaw Jun 28, 2023
7a58a19
[lightapi/python] task: add Block model
AnthonyLaw Jun 28, 2023
b091896
[lightapi/python] task: replace data to json
AnthonyLaw Jul 4, 2023
8ffbf4b
[lightapi/python] task: add mapping transaction, TransferTransaction,…
AnthonyLaw Jul 12, 2023
291a2b1
[lightapi/python] task: refactor on transaction model
AnthonyLaw Jul 14, 2023
4ae2196
[lightapi/python] task: add new model NamespaceRegistrationTransacti…
AnthonyLaw Jul 16, 2023
5d9789f
[lightapi/python] task: refactor and support more transaction
AnthonyLaw Jul 16, 2023
53fc511
[lightapi/python] task: add new model MultisigTransaction and CosignS…
AnthonyLaw Jul 17, 2023
d660f43
[lightapi/python] task: refactor on transaction model and nem connector
AnthonyLaw Jul 18, 2023
5c94730
[lightapi/python] task: rename transaction model
AnthonyLaw Jul 18, 2023
af1a8d2
[lightapi/python] task: add UnknownTransactionType exception
AnthonyLaw Jul 18, 2023
1d6ea50
[lightapi/python] task: rename field
AnthonyLaw Jul 21, 2023
8af4b6a
[lightapi/python] task: refactor, add transaction handler
AnthonyLaw Jul 21, 2023
9780a73
[lightapi/python] task: set minCosignatories to 0 if properties not e…
AnthonyLaw Jul 22, 2023
7b9f367
[lightapi/python] build: update python sdk 3.0.11
AnthonyLaw Jul 31, 2023
69cf69f
[lightapi/python] task: add binary response type support
AnthonyLaw Sep 13, 2023
f084990
[lightapi/python] task: add new endpoint to get block size
AnthonyLaw Sep 13, 2023
d09f425
[lightapi/python] fix: Handle none in transfer message and relativeCh…
AnthonyLaw Sep 14, 2023
8bc15f3
[lightapi/python] task: add unit test for query block size
AnthonyLaw Oct 4, 2023
c811aa1
[lightapi/python] fix: invalid string quote
AnthonyLaw Oct 4, 2023
cfaa563
[lightapi/python] task: add unit test to all transactions type
AnthonyLaw Oct 5, 2023
318344c
[lightapi/python] task: add unit test to Transaction Factory
AnthonyLaw Oct 5, 2023
c908483
[lightapi/python] fix: disable not-callable in lint
AnthonyLaw Oct 5, 2023
8449846
[lightapi/python] task: removed unused field
AnthonyLaw Oct 6, 2023
5569c66
[lightapi/python] task: refactor on transaction mapper
AnthonyLaw Oct 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions lightapi/python/symbollightapi/connector/BasicConnector.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,27 @@ def __init__(self, endpoint):
self.endpoint = endpoint
self.timeout_seconds = None

async def _dispatch(self, action, url_path, property_name, **kwargs):
async def _dispatch(self, action, url_path, property_name=None, response_type='json', **kwargs):
headers = kwargs.get('headers', {})

if response_type == 'binary':
headers['Accept'] = 'application/binary'

kwargs['headers'] = headers

try:
timeout = ClientTimeout(total=self.timeout_seconds)
async with ClientSession(timeout=timeout) as session:
async with getattr(session, action)(f'{self.endpoint}/{url_path}', **kwargs) as response:
try:
response_json = await response.json()
except (client_exceptions.ContentTypeError, json.decoder.JSONDecodeError) as ex:
raise NodeException from ex
if response_type == 'json':
try:
response_json = await response.json()
except (client_exceptions.ContentTypeError, json.decoder.JSONDecodeError) as ex:
raise NodeException from ex
elif response_type == 'binary':
response_json = await response.read()
else:
raise ValueError(f'Unsupported response type: {response_type}')

if response.status not in (200, 404):
error_message = f'HTTP request failed with code {response.status}'
Expand All @@ -33,7 +45,10 @@ async def _dispatch(self, action, url_path, property_name, **kwargs):

raise NodeException(error_message)

return response_json if property_name is None else response_json[property_name]
if response_type == 'json':
return response_json if property_name is None else response_json[property_name]
return response_json

except (asyncio.TimeoutError, client_exceptions.ClientConnectorError) as ex:
raise NodeException from ex

Expand All @@ -51,6 +66,12 @@ async def put(self, url_path, request_payload, property_name=None):
Raises NodeException on connection or content failure.
"""

return await self._dispatch('put', url_path, property_name, data=json.dumps(request_payload), headers={
'Content-Type': 'application/json'
})
return await self._dispatch('put', url_path, property_name, json=request_payload)

async def post(self, url_path, request_payload, property_name=None, response_type='json'):
"""
Initiates a POST to the specified path and returns the desired property.
Raises NodeException on connection or content failure.
"""

return await self._dispatch('post', url_path, property_name, json=request_payload, response_type=response_type)
79 changes: 79 additions & 0 deletions lightapi/python/symbollightapi/connector/NemConnector.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import asyncio

from symbolchain.CryptoTypes import PublicKey
from symbolchain.nem.Network import Address

from ..model.Block import Block
from ..model.Endpoint import Endpoint
from ..model.NodeInfo import NodeInfo
from ..model.Transaction import TransactionFactory, TransactionMapperFactory
from .BasicConnector import BasicConnector

MICROXEM_PER_XEM = 1000000
Expand Down Expand Up @@ -91,3 +95,78 @@ def _map_to_node_info(node_dict):
node_dict['identity']['name'],
node_dict['metaData']['version'],
NodeInfo.API_ROLE_FLAG)

async def get_blocks_after(self, height):
""""Gets Blocks data"""

blocks = await self.post('local/chain/blocks-after', {'height': height})

block_heights = [block['block']['height'] for block in blocks['data']]
block_sizes = await asyncio.gather(*[self.get_block_size(height) for height in block_heights])

for block, size in zip(blocks['data'], block_sizes):
block['size'] = size

return [self._map_to_block(block) for block in blocks['data']]

async def get_block(self, height):
""""Gets Block data"""

block = await self.post('local/block/at', {'height': height})

block_sizes = await self.get_block_size(height)
block['size'] = block_sizes

return self._map_to_block(block)

async def get_block_size(self, height):
""""Gets Block size"""

block_size = await self.post('block/at/public', {'height': height}, response_type='binary')
return len(block_size)

def _map_to_block(self, block_dict):
block = block_dict['block']
difficulty = block_dict['difficulty']
block_hash = block_dict['hash']
transactions = block_dict['txes']
size = block_dict['size']

return Block(
block['height'],
block['timeStamp'],
[
self._map_to_transaction(transaction, block['height'])
for transaction in transactions
],
difficulty,
block_hash,
block['signer'],
block['signature'],
size
)

@staticmethod
def _map_to_transaction(transaction, block_height):
"""Maps a transaction dictionary to a transaction object."""

tx_dict = transaction['tx']
tx_type = tx_dict['type']

# Define common arguments for all transactions
common_args = {
'transaction_hash': transaction['hash'],
'height': block_height,
'sender': tx_dict['signer'],
'fee': tx_dict['fee'],
'timestamp': tx_dict['timeStamp'],
'deadline': tx_dict['deadline'],
'signature': tx_dict['signature'],
}

specific_args = {}

mapper = TransactionMapperFactory.get_mapper(tx_type)
specific_args = mapper.map_transaction(tx_dict)

return TransactionFactory.create_transaction(tx_type, common_args, specific_args)
28 changes: 28 additions & 0 deletions lightapi/python/symbollightapi/model/Block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Block:
"""Block model."""

def __init__(self, height, timestamp, transactions, difficulty, block_hash, signer, signature, size):
"""Create a Block model."""

# pylint: disable=too-many-arguments

self.height = height
self.timestamp = timestamp
self.transactions = transactions
self.difficulty = difficulty
self.block_hash = block_hash
self.signer = signer
self.signature = signature
self.size = size

def __eq__(self, other):
return isinstance(other, Block) and all([
self.height == other.height,
self.timestamp == other.timestamp,
self.transactions == other.transactions,
self.difficulty == other.difficulty,
self.block_hash == other.block_hash,
self.signer == other.signer,
self.signature == other.signature,
self.size == other.size
])
4 changes: 4 additions & 0 deletions lightapi/python/symbollightapi/model/Exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ class NodeException(Exception):

class CorruptDataException(NodeException):
"""Exception raised when corrupt data is received from a node."""


class UnknownTransactionType(Exception):
"""Exception raised when Unknown transaction type."""
Loading