mirror of
https://github.com/LBRYFoundation/lbry-sdk.git
synced 2025-09-01 01:35:14 +00:00
meaningful API error messages
This commit is contained in:
parent
c407d32f5d
commit
ec4f9011b9
2 changed files with 57 additions and 49 deletions
|
@ -1,15 +1,17 @@
|
||||||
import sys
|
import sys
|
||||||
import json
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
from lbrynet.conf import settings
|
from lbrynet.conf import settings
|
||||||
from lbrynet.lbrynet_daemon.auth.client import LBRYAPIClient
|
from lbrynet.lbrynet_daemon.auth.client import LBRYAPIClient
|
||||||
|
from jsonrpc.common import RPCError
|
||||||
|
|
||||||
help_msg = "Usage: lbrynet-cli method json-args\n" \
|
|
||||||
|
|
||||||
|
help_msg = "Usage: lbrynet-cli method kwargs\n" \
|
||||||
+ "Examples: " \
|
+ "Examples: " \
|
||||||
+ "lbrynet-cli resolve_name '{\"name\": \"what\"}'\n" \
|
+ "lbrynet-cli resolve_name name=what\n" \
|
||||||
+ "lbrynet-cli get_balance\n" \
|
+ "lbrynet-cli get_balance\n" \
|
||||||
+ "lbrynet-cli help '{\"function\": \"resolve_name\"}'\n" \
|
+ "lbrynet-cli help function=resolve_name\n" \
|
||||||
+ "\n******lbrynet-cli functions******\n"
|
+ "\n******lbrynet-cli functions******\n"
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +61,6 @@ def main():
|
||||||
meth = args.method[0]
|
meth = args.method[0]
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
if args.params:
|
|
||||||
if len(args.params) > 1:
|
if len(args.params) > 1:
|
||||||
params = get_params_from_kwargs(args.params)
|
params = get_params_from_kwargs(args.params)
|
||||||
elif len(args.params) == 1:
|
elif len(args.params) == 1:
|
||||||
|
@ -83,14 +84,16 @@ def main():
|
||||||
else:
|
else:
|
||||||
result = LBRYAPIClient.config(service=meth, params=params)
|
result = LBRYAPIClient.config(service=meth, params=params)
|
||||||
print json.dumps(result, sort_keys=True)
|
print json.dumps(result, sort_keys=True)
|
||||||
except:
|
except RPCError as err:
|
||||||
# TODO: The api should return proper error codes
|
# TODO: The api should return proper error codes
|
||||||
# and messages so that they can be passed along to the user
|
# and messages so that they can be passed along to the user
|
||||||
# instead of this generic message.
|
# instead of this generic message.
|
||||||
# https://app.asana.com/0/158602294500137/200173944358192
|
# https://app.asana.com/0/158602294500137/200173944358192
|
||||||
|
|
||||||
print "Something went wrong, here's the usage for %s:" % meth
|
print "Something went wrong, here's the usage for %s:" % meth
|
||||||
print api.help({'function': meth})
|
print api.help({'function': meth})
|
||||||
|
print "Here's the traceback for the error you encountered:"
|
||||||
|
print err.msg
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print "Unknown function"
|
print "Unknown function"
|
||||||
print msg
|
print msg
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from twisted.web import server, resource
|
from twisted.web import server, resource
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
from twisted.python.failure import Failure
|
||||||
|
|
||||||
from txjsonrpc import jsonrpclib
|
from txjsonrpc import jsonrpclib
|
||||||
|
|
||||||
from lbrynet.core.Error import InvalidAuthenticationToken, InvalidHeaderError, SubhandlerError
|
from lbrynet.core.Error import InvalidAuthenticationToken, InvalidHeaderError, SubhandlerError
|
||||||
|
@ -19,6 +20,12 @@ def default_decimal(obj):
|
||||||
return float(obj)
|
return float(obj)
|
||||||
|
|
||||||
|
|
||||||
|
class JSONRPCException(Exception):
|
||||||
|
def __init__(self, err, code):
|
||||||
|
self.faultCode = code
|
||||||
|
self.faultString = err.getTraceback()
|
||||||
|
|
||||||
|
|
||||||
class AuthorizedBase(object):
|
class AuthorizedBase(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.authorized_functions = []
|
self.authorized_functions = []
|
||||||
|
@ -90,7 +97,16 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
return NotImplementedError()
|
return NotImplementedError()
|
||||||
|
|
||||||
|
def _render_error(self, request, failure, version=jsonrpclib.VERSION_1, response_code=FAILURE):
|
||||||
|
fault = jsonrpclib.dumps(JSONRPCException(Failure(failure), response_code), version=version)
|
||||||
|
self._set_headers(request, fault)
|
||||||
|
if response_code != AuthJSONRPCServer.FAILURE:
|
||||||
|
request.setResponseCode(response_code)
|
||||||
|
request.write(fault)
|
||||||
|
request.finish()
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
|
notify_finish = request.notifyFinish()
|
||||||
assert self._check_headers(request), InvalidHeaderError
|
assert self._check_headers(request), InvalidHeaderError
|
||||||
|
|
||||||
session = request.getSession()
|
session = request.getSession()
|
||||||
|
@ -114,8 +130,10 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
content = request.content.read()
|
content = request.content.read()
|
||||||
try:
|
try:
|
||||||
parsed = jsonrpclib.loads(content)
|
parsed = jsonrpclib.loads(content)
|
||||||
except ValueError:
|
except ValueError as err:
|
||||||
return server.failure
|
log.error("Unable to decode request json")
|
||||||
|
self._render_error(request, err)
|
||||||
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
function_name = parsed.get('method')
|
function_name = parsed.get('method')
|
||||||
args = parsed.get('params')
|
args = parsed.get('params')
|
||||||
|
@ -125,36 +143,35 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._run_subhandlers(request)
|
self._run_subhandlers(request)
|
||||||
except SubhandlerError:
|
except SubhandlerError as err:
|
||||||
return server.failure
|
self._render_error(request, err, version)
|
||||||
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
reply_with_next_secret = False
|
reply_with_next_secret = False
|
||||||
if self._use_authentication:
|
if self._use_authentication:
|
||||||
if function_name in self.authorized_functions:
|
if function_name in self.authorized_functions:
|
||||||
try:
|
try:
|
||||||
self._verify_token(session_id, parsed, token)
|
self._verify_token(session_id, parsed, token)
|
||||||
except InvalidAuthenticationToken:
|
except InvalidAuthenticationToken as err:
|
||||||
log.warning("API validation failed")
|
log.error("API validation failed")
|
||||||
request.setResponseCode(self.UNAUTHORIZED)
|
self._render_error(request, err, version, response_code=AuthJSONRPCServer.UNAUTHORIZED)
|
||||||
request.finish()
|
|
||||||
return server.NOT_DONE_YET
|
return server.NOT_DONE_YET
|
||||||
self._update_session_secret(session_id)
|
self._update_session_secret(session_id)
|
||||||
reply_with_next_secret = True
|
reply_with_next_secret = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
function = self._get_jsonrpc_method(function_name)
|
function = self._get_jsonrpc_method(function_name)
|
||||||
except Exception:
|
except AttributeError as err:
|
||||||
log.warning("Unknown method: %s", function_name)
|
log.error("Unknown method: %s", function_name)
|
||||||
return server.failure
|
self._render_error(request, err, version)
|
||||||
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
d = defer.maybeDeferred(function) if args == [{}] else defer.maybeDeferred(function, *args)
|
d = defer.maybeDeferred(function) if args == [{}] else defer.maybeDeferred(function, *args)
|
||||||
# cancel the response if the connection is broken
|
|
||||||
notify_finish = request.notifyFinish()
|
|
||||||
notify_finish.addErrback(self._response_failed, d)
|
|
||||||
d.addErrback(self._errback_render, id)
|
|
||||||
d.addCallback(self._callback_render, request, id, version, reply_with_next_secret)
|
|
||||||
d.addErrback(notify_finish.errback)
|
|
||||||
|
|
||||||
|
# cancel the response if the connection is broken
|
||||||
|
notify_finish.addErrback(self._response_failed, d)
|
||||||
|
d.addCallback(self._callback_render, request, version, reply_with_next_secret)
|
||||||
|
d.addErrback(lambda err: self._render_error(request, err, version))
|
||||||
return server.NOT_DONE_YET
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
def _register_user_session(self, session_id):
|
def _register_user_session(self, session_id):
|
||||||
|
@ -209,7 +226,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _get_jsonrpc_method(self, function_path):
|
def _get_jsonrpc_method(self, function_path):
|
||||||
assert self._check_function_path(function_path)
|
assert self._check_function_path(function_path), AttributeError(function_path)
|
||||||
return self.callable_methods.get(function_path)
|
return self.callable_methods.get(function_path)
|
||||||
|
|
||||||
def _initialize_session(self, session_id):
|
def _initialize_session(self, session_id):
|
||||||
|
@ -242,9 +259,9 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
assert handler(request)
|
assert handler(request)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
log.error(err.message)
|
log.error(err.message)
|
||||||
raise SubhandlerError
|
raise SubhandlerError(err.message)
|
||||||
|
|
||||||
def _callback_render(self, result, request, id, version, auth_required=False):
|
def _callback_render(self, result, request, version, auth_required=False):
|
||||||
result_for_return = result if not isinstance(result, dict) else result['result']
|
result_for_return = result if not isinstance(result, dict) else result['result']
|
||||||
|
|
||||||
if version == jsonrpclib.VERSION_PRE1:
|
if version == jsonrpclib.VERSION_PRE1:
|
||||||
|
@ -255,20 +272,8 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
encoded_message = jsonrpclib.dumps(result_for_return, version=version, default=default_decimal)
|
encoded_message = jsonrpclib.dumps(result_for_return, version=version, default=default_decimal)
|
||||||
self._set_headers(request, encoded_message, auth_required)
|
self._set_headers(request, encoded_message, auth_required)
|
||||||
self._render_message(request, encoded_message)
|
self._render_message(request, encoded_message)
|
||||||
except:
|
except Exception as err:
|
||||||
fault = jsonrpclib.Fault(self.FAILURE, "can't serialize output")
|
self._render_error(request, err, response_code=self.FAILURE, version=version)
|
||||||
encoded_message = jsonrpclib.dumps(fault, version=version)
|
|
||||||
self._set_headers(request, encoded_message)
|
|
||||||
self._render_message(request, encoded_message)
|
|
||||||
|
|
||||||
def _errback_render(self, failure, id):
|
|
||||||
log.error("Request failed:")
|
|
||||||
log.error(failure)
|
|
||||||
log.error(failure.value)
|
|
||||||
log.error(id)
|
|
||||||
if isinstance(failure.value, jsonrpclib.Fault):
|
|
||||||
return failure.value
|
|
||||||
return server.failure
|
|
||||||
|
|
||||||
def _render_response(self, result, code):
|
def _render_response(self, result, code):
|
||||||
return defer.succeed({'result': result, 'code': code})
|
return defer.succeed({'result': result, 'code': code})
|
||||||
|
|
Loading…
Add table
Reference in a new issue