forked from LBRYCommunity/lbry-sdk
change returned error data structure to be JSONRPC standard compliant
This commit is contained in:
parent
9f0fba063d
commit
57ee16d565
1 changed files with 43 additions and 40 deletions
|
@ -212,44 +212,46 @@ class JSONRPCError:
|
||||||
CODE_AUTHENTICATION_ERROR: 401,
|
CODE_AUTHENTICATION_ERROR: 401,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, message, code=CODE_APPLICATION_ERROR, traceback=None, data=None, name=None):
|
def __init__(self, code: int, message: str, data: dict=None):
|
||||||
assert isinstance(code, int), "'code' must be an int"
|
assert code and isinstance(code, int), "'code' must be an int"
|
||||||
assert (data is None or isinstance(data, dict)), "'data' must be None or a dict"
|
assert message and isinstance(message, str), "'message' must be a string"
|
||||||
|
assert data is None or isinstance(data, dict), "'data' must be None or a dict"
|
||||||
self.code = code
|
self.code = code
|
||||||
self.name = name or 'Exception'
|
|
||||||
if message is None:
|
|
||||||
message = self.MESSAGES[code] if code in self.MESSAGES else "API Error"
|
|
||||||
self.message = message
|
self.message = message
|
||||||
self.data = {} if data is None else data
|
self.data = data or {}
|
||||||
self.traceback = []
|
|
||||||
if traceback is not None:
|
|
||||||
self.traceback = trace_lines = traceback.split("\n")
|
|
||||||
for i, t in enumerate(trace_lines):
|
|
||||||
if "--- <exception caught here> ---" in t:
|
|
||||||
if len(trace_lines) > i + 1:
|
|
||||||
self.traceback = [j for j in trace_lines[i + 1:] if j]
|
|
||||||
break
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
'code': self.code,
|
'code': self.code,
|
||||||
'name': self.name,
|
|
||||||
'message': self.message,
|
'message': self.message,
|
||||||
'data': self.data,
|
'data': self.data,
|
||||||
'traceback': self.traceback
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def create_command_exception(cls, e, command, args, kwargs, traceback):
|
def filter_traceback(traceback):
|
||||||
if 'password' in kwargs and isinstance(kwargs['password'], str):
|
result = []
|
||||||
kwargs['password'] = '*'*len(kwargs['password'])
|
if traceback is not None:
|
||||||
return cls(str(e), traceback=traceback, name=e.__class__.__name__, data={
|
result = trace_lines = traceback.split("\n")
|
||||||
'command': command, 'args': args, 'kwargs': kwargs,
|
for i, t in enumerate(trace_lines):
|
||||||
})
|
if "--- <exception caught here> ---" in t:
|
||||||
|
if len(trace_lines) > i + 1:
|
||||||
|
result = [j for j in trace_lines[i + 1:] if j]
|
||||||
|
break
|
||||||
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_rpc_exception(cls, e, code):
|
def create_command_exception(cls, command, args, kwargs, exception, traceback):
|
||||||
return cls(str(e), name=e.__class__.__name__, code=code)
|
if 'password' in kwargs and isinstance(kwargs['password'], str):
|
||||||
|
kwargs['password'] = '*'*len(kwargs['password'])
|
||||||
|
return cls(
|
||||||
|
cls.CODE_APPLICATION_ERROR, str(exception), {
|
||||||
|
'name': exception.__class__.__name__,
|
||||||
|
'traceback': cls.filter_traceback(traceback),
|
||||||
|
'command': command,
|
||||||
|
'args': args,
|
||||||
|
'kwargs': kwargs,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UnknownAPIMethodError(Exception):
|
class UnknownAPIMethodError(Exception):
|
||||||
|
@ -514,8 +516,9 @@ class Daemon(metaclass=JSONRPCServerType):
|
||||||
except:
|
except:
|
||||||
log.exception('Failed to encode JSON RPC result:')
|
log.exception('Failed to encode JSON RPC result:')
|
||||||
encoded_result = jsonrpc_dumps_pretty(JSONRPCError(
|
encoded_result = jsonrpc_dumps_pretty(JSONRPCError(
|
||||||
|
JSONRPCError.CODE_APPLICATION_ERROR,
|
||||||
'After successfully executing the command, failed to encode result for JSON RPC response.',
|
'After successfully executing the command, failed to encode result for JSON RPC response.',
|
||||||
JSONRPCError.CODE_APPLICATION_ERROR, format_exc()
|
{'traceback': format_exc()}
|
||||||
), ledger=ledger)
|
), ledger=ledger)
|
||||||
return web.Response(
|
return web.Response(
|
||||||
text=encoded_result,
|
text=encoded_result,
|
||||||
|
@ -569,17 +572,17 @@ class Daemon(metaclass=JSONRPCServerType):
|
||||||
try:
|
try:
|
||||||
function_name = data['method']
|
function_name = data['method']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return JSONRPCError.create_rpc_exception(
|
return JSONRPCError(
|
||||||
CommandError("Missing 'method' value in request."),
|
JSONRPCError.CODE_METHOD_NOT_FOUND,
|
||||||
JSONRPCError.CODE_METHOD_NOT_FOUND
|
"Missing 'method' value in request."
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fn = self._get_jsonrpc_method(function_name)
|
fn = self._get_jsonrpc_method(function_name)
|
||||||
except UnknownAPIMethodError:
|
except UnknownAPIMethodError:
|
||||||
return JSONRPCError.create_rpc_exception(
|
return JSONRPCError(
|
||||||
CommandDoesNotExistError(function_name),
|
JSONRPCError.CODE_METHOD_NOT_FOUND,
|
||||||
JSONRPCError.CODE_METHOD_NOT_FOUND
|
str(CommandDoesNotExistError(function_name))
|
||||||
)
|
)
|
||||||
|
|
||||||
if args in ([{}], []):
|
if args in ([{}], []):
|
||||||
|
@ -594,9 +597,9 @@ class Daemon(metaclass=JSONRPCServerType):
|
||||||
isinstance(args[0], list) and isinstance(args[1], dict):
|
isinstance(args[0], list) and isinstance(args[1], dict):
|
||||||
_args, _kwargs = args
|
_args, _kwargs = args
|
||||||
else:
|
else:
|
||||||
return JSONRPCError.create_rpc_exception(
|
return JSONRPCError(
|
||||||
CommandError(f"Invalid parameters format: {args}"),
|
JSONRPCError.CODE_INVALID_PARAMS,
|
||||||
JSONRPCError.CODE_INVALID_PARAMS
|
f"Invalid parameters format: {args}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_transactional_function(function_name):
|
if is_transactional_function(function_name):
|
||||||
|
@ -608,9 +611,9 @@ class Daemon(metaclass=JSONRPCServerType):
|
||||||
params_error, function_name, ', '.join(erroneous_params)
|
params_error, function_name, ', '.join(erroneous_params)
|
||||||
)
|
)
|
||||||
log.warning(params_error_message)
|
log.warning(params_error_message)
|
||||||
return JSONRPCError.create_rpc_exception(
|
return JSONRPCError(
|
||||||
CommandError(params_error_message),
|
JSONRPCError.CODE_INVALID_PARAMS,
|
||||||
JSONRPCError.CODE_INVALID_PARAMS
|
params_error_message,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -624,7 +627,7 @@ class Daemon(metaclass=JSONRPCServerType):
|
||||||
except Exception as e: # pylint: disable=broad-except
|
except Exception as e: # pylint: disable=broad-except
|
||||||
log.exception("error handling api request")
|
log.exception("error handling api request")
|
||||||
return JSONRPCError.create_command_exception(
|
return JSONRPCError.create_command_exception(
|
||||||
e, function_name, _args, _kwargs, format_exc()
|
command=function_name, args=_args, kwargs=_kwargs, exception=e, traceback=format_exc()
|
||||||
)
|
)
|
||||||
|
|
||||||
def _verify_method_is_callable(self, function_path):
|
def _verify_method_is_callable(self, function_path):
|
||||||
|
|
Loading…
Reference in a new issue