chainlib/chainlib/jsonrpc.py

167 lines
4.3 KiB
Python
Raw Normal View History

2021-04-04 14:55:27 +02:00
# standard imports
import uuid
# local imports
from .error import JSONRPCException
2021-08-21 09:31:59 +02:00
# TODO: Move all contents in this file to independent package
2021-04-04 14:55:27 +02:00
2021-08-21 09:31:59 +02:00
class JSONRPCIdGenerator:
def next(self):
raise NotImplementedError
class UUIDGenerator(JSONRPCIdGenerator):
"""Create uuid ids for JSON-RPC queries.
"""
def next(self):
"""Create a new id
:rtype: str
:returns: uuid string
"""
return str(uuid.uuid4())
class IntSequenceGenerator(JSONRPCIdGenerator):
"""Create sequential numeric ids for JSON-RPC queries.
:param start: Start at the specificed numeric id
:type start: int
"""
def __init__(self, start=0):
self.id = start
def next(self):
"""Get the next id in the sequence.
:rtype: int
:returns: numeric id
"""
next_id = self.id
self.id += 1
return next_id
default_id_generator = UUIDGenerator()
class ErrorParser:
"""Base class for parsing JSON-RPC error repsonses
"""
2021-04-04 14:55:27 +02:00
def translate(self, error):
2021-08-21 09:31:59 +02:00
"""Interface method called by jsonrpc_result when encountering an error
This class method may be overriden to provide more fine-grained context for both general and implementation specific errors.
:param error: JSON-RPC error response object
:type error: dict
:rtype: chainlib.error.JSONRPCException
:returns: Descriptiv JSONRPCException
"""
2021-04-04 14:55:27 +02:00
return JSONRPCException('default parser code {}'.format(error))
2021-08-21 09:31:59 +02:00
# deprecated symbol, provided for backward compatibility
DefaultErrorParser = ErrorParser
2021-04-04 14:55:27 +02:00
2021-08-21 09:31:59 +02:00
class JSONRPCRequest:
"""JSON-RPC request builder class.
:param id_generator: Generator to use to define the id of the request.
:type id_generator: chainlib.jsonrpc.JSONRPCIdGenerator
"""
def __init__(self, id_generator=default_id_generator):
if id_generator == None:
id_generator = default_id_generator
self.id_generator = id_generator
def template(self):
"""Return a empty json-rpc 2.0 dictionary query object
:rtype: dict
:returns: json-rpc query object
"""
return {
'jsonrpc': '2.0',
'id': None,
'method': None,
'params': [],
}
def finalize(self, request):
"""Apply next json-rpc id to json-rpc dictionary query object
:param request: json-rpc query
:type request: dict
:rtype: dict
:returns: json-rpc query with id added
"""
request['id'] = self.id_generator.next()
return request
2021-04-04 14:55:27 +02:00
def jsonrpc_response(request_id, result):
2021-08-21 09:31:59 +02:00
"""Create a json-rpc dictionary response object from the given id an result value.
:param request_id: json-rpc query id
:type request_id: str or int
:param result: result value
:type result: any json-serializable value
:rtype: dict
:result: json-rpc response object
"""
return {
'jsonrpc': '2.0',
'id': request_id,
'result': result,
}
2021-04-04 14:55:27 +02:00
def jsonrpc_error(request_id, code=-32000, message='Server error'):
2021-08-21 09:31:59 +02:00
"""Create a json-rpc dictionary error object for the given id with error code and message.
:param request_id: json-rpc query id
:type request_id: str or int
:param code: json-rpc error code
:type code: int
:param message: Error message
:type message: str
:rtype: dict
:returns: json-rpc error object
"""
2021-04-04 14:55:27 +02:00
return {
'jsonrpc': '2.0',
'id': request_id,
'error': {
'code': code,
'message': message,
},
}
2021-08-21 09:31:59 +02:00
def jsonrpc_result(o, ep):
"""Retrieve the result from a json-rpc response object.
If the result object is an error, the provided error parser will be used to generate the corresponding exception.
:param o: json-rpc response object
:type o: dict
:param ep: Error parser
:type ep: chainlib.jsonrpc.ErrorParser
:raises JSONRPCException: exception encapsulating the error value of the response
:rtype: any json-deserializable value
:returns: The result value of the response
"""
if o.get('error') != None:
raise ep.translate(o)
return o['result']