api: fixes and changes to always return content type in API
- few tweeks here and there
This commit is contained in:
parent
745c3af99f
commit
047254866b
6 changed files with 67 additions and 58 deletions
|
|
@ -21,10 +21,10 @@
|
|||
import itertools
|
||||
import logging
|
||||
import sys
|
||||
import types
|
||||
import fnmatch
|
||||
|
||||
import decorator
|
||||
import typing
|
||||
import venusian
|
||||
from collections import OrderedDict
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ from rhodecode.apps._base import TemplateArgs
|
|||
from rhodecode.lib.auth import AuthUser
|
||||
from rhodecode.lib.base import get_ip_addr, attach_context_attributes
|
||||
from rhodecode.lib.exc_tracking import store_exception
|
||||
from rhodecode.lib.ext_json import json
|
||||
from rhodecode.lib import ext_json
|
||||
from rhodecode.lib.utils2 import safe_str
|
||||
from rhodecode.lib.plugins.utils import get_plugin_settings
|
||||
from rhodecode.model.db import User, UserApiKeys
|
||||
|
|
@ -64,15 +64,12 @@ def find_methods(jsonrpc_methods, pattern):
|
|||
|
||||
class ExtJsonRenderer(object):
|
||||
"""
|
||||
Custom renderer that mkaes use of our ext_json lib
|
||||
Custom renderer that makes use of our ext_json lib
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, serializer=json.dumps, **kw):
|
||||
""" Any keyword arguments will be passed to the ``serializer``
|
||||
function."""
|
||||
self.serializer = serializer
|
||||
self.kw = kw
|
||||
def __init__(self):
|
||||
self.serializer = ext_json.formatted_json
|
||||
|
||||
def __call__(self, info):
|
||||
""" Returns a plain JSON-encoded string with content-type
|
||||
|
|
@ -87,25 +84,17 @@ class ExtJsonRenderer(object):
|
|||
if ct == response.default_content_type:
|
||||
response.content_type = 'application/json'
|
||||
|
||||
return self.serializer(value, **self.kw)
|
||||
return self.serializer(value)
|
||||
|
||||
return _render
|
||||
|
||||
|
||||
def jsonrpc_response(request, result):
|
||||
rpc_id = getattr(request, 'rpc_id', None)
|
||||
response = request.response
|
||||
|
||||
# store content_type before render is called
|
||||
ct = response.content_type
|
||||
|
||||
ret_value = ''
|
||||
if rpc_id:
|
||||
ret_value = {
|
||||
'id': rpc_id,
|
||||
'result': result,
|
||||
'error': None,
|
||||
}
|
||||
ret_value = {'id': rpc_id, 'result': result, 'error': None}
|
||||
|
||||
# fetch deprecation warnings, and store it inside results
|
||||
deprecation = getattr(request, 'rpc_deprecation', None)
|
||||
|
|
@ -113,30 +102,36 @@ def jsonrpc_response(request, result):
|
|||
ret_value['DEPRECATION_WARNING'] = deprecation
|
||||
|
||||
raw_body = render(DEFAULT_RENDERER, ret_value, request=request)
|
||||
response.body = safe_str(raw_body, response.charset)
|
||||
|
||||
if ct == response.default_content_type:
|
||||
response.content_type = 'application/json'
|
||||
|
||||
return response
|
||||
content_type = 'application/json'
|
||||
content_type_header = 'Content-Type'
|
||||
headers = {
|
||||
content_type_header: content_type
|
||||
}
|
||||
return Response(
|
||||
body=raw_body,
|
||||
content_type=content_type,
|
||||
headerlist=[(k, v) for k, v in headers.items()]
|
||||
)
|
||||
|
||||
|
||||
def jsonrpc_error(request, message, retid=None, code=None, headers=None):
|
||||
def jsonrpc_error(request, message, retid=None, code: typing.Optional[int] = None, headers: typing.Optional[dict] = None):
|
||||
"""
|
||||
Generate a Response object with a JSON-RPC error body
|
||||
|
||||
:param code:
|
||||
:param retid:
|
||||
:param message:
|
||||
"""
|
||||
headers = headers or {}
|
||||
content_type = 'application/json'
|
||||
content_type_header = 'Content-Type'
|
||||
if content_type_header not in headers:
|
||||
headers[content_type_header] = content_type
|
||||
|
||||
err_dict = {'id': retid, 'result': None, 'error': message}
|
||||
body = render(DEFAULT_RENDERER, err_dict, request=request).encode('utf-8')
|
||||
raw_body = render(DEFAULT_RENDERER, err_dict, request=request)
|
||||
|
||||
return Response(
|
||||
body=body,
|
||||
body=raw_body,
|
||||
status=code,
|
||||
content_type='application/json',
|
||||
headerlist=headers
|
||||
content_type=content_type,
|
||||
headerlist=[(k, v) for k, v in headers.items()]
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -158,11 +153,11 @@ def exception_view(exc, request):
|
|||
method = request.rpc_method
|
||||
log.debug('json-rpc method `%s` not found in list of '
|
||||
'api calls: %s, rpc_id:%s',
|
||||
method, request.registry.jsonrpc_methods.keys(), rpc_id)
|
||||
method, list(request.registry.jsonrpc_methods.keys()), rpc_id)
|
||||
|
||||
similar = 'none'
|
||||
try:
|
||||
similar_paterns = ['*{}*'.format(x) for x in method.split('_')]
|
||||
similar_paterns = [f'*{x}*' for x in method.split('_')]
|
||||
similar_found = find_methods(
|
||||
request.registry.jsonrpc_methods, similar_paterns)
|
||||
similar = ', '.join(similar_found.keys()) or similar
|
||||
|
|
@ -222,7 +217,7 @@ def request_view(request):
|
|||
|
||||
# register our auth-user
|
||||
request.rpc_user = auth_u
|
||||
request.environ['rc_auth_user_id'] = auth_u.user_id
|
||||
request.environ['rc_auth_user_id'] = str(auth_u.user_id)
|
||||
|
||||
# now check if token is valid for API
|
||||
auth_token = request.rpc_api_key
|
||||
|
|
@ -246,10 +241,12 @@ def request_view(request):
|
|||
|
||||
# now that we have a method, add request._req_params to
|
||||
# self.kargs and dispatch control to WGIController
|
||||
|
||||
argspec = inspect.getargspec(func)
|
||||
arglist = argspec[0]
|
||||
defaults = map(type, argspec[3] or [])
|
||||
default_empty = types.NotImplementedType
|
||||
defs = argspec[3] or []
|
||||
defaults = [type(a) for a in defs]
|
||||
default_empty = type(NotImplemented)
|
||||
|
||||
# kw arguments required by this method
|
||||
func_kwargs = dict(itertools.zip_longest(
|
||||
|
|
@ -285,7 +282,7 @@ def request_view(request):
|
|||
)
|
||||
|
||||
# sanitize extra passed arguments
|
||||
for k in request.rpc_params.keys()[:]:
|
||||
for k in list(request.rpc_params.keys()):
|
||||
if k not in func_kwargs:
|
||||
del request.rpc_params[k]
|
||||
|
||||
|
|
@ -313,8 +310,10 @@ def request_view(request):
|
|||
exc_info = sys.exc_info()
|
||||
exc_id, exc_type_name = store_exception(
|
||||
id(exc_info), exc_info, prefix='rhodecode-api')
|
||||
error_headers = [('RhodeCode-Exception-Id', str(exc_id)),
|
||||
('RhodeCode-Exception-Type', str(exc_type_name))]
|
||||
error_headers = {
|
||||
'RhodeCode-Exception-Id': str(exc_id),
|
||||
'RhodeCode-Exception-Type': str(exc_type_name)
|
||||
}
|
||||
err_resp = jsonrpc_error(
|
||||
request, retid=request.rpc_id, message='Internal server error',
|
||||
headers=error_headers)
|
||||
|
|
@ -355,7 +354,7 @@ def setup_request(request):
|
|||
raw_body = request.body
|
||||
log.debug("Loading JSON body now")
|
||||
try:
|
||||
json_body = json.loads(raw_body)
|
||||
json_body = ext_json.json.loads(raw_body)
|
||||
except ValueError as e:
|
||||
# catch JSON errors Here
|
||||
raise JSONRPCError("JSON parse error ERR:%s RAW:%r" % (e, raw_body))
|
||||
|
|
@ -382,7 +381,7 @@ def setup_request(request):
|
|||
|
||||
log.debug('method: %s, params: %.10240r', request.rpc_method, request.rpc_params)
|
||||
except KeyError as e:
|
||||
raise JSONRPCError('Incorrect JSON data. Missing %s' % e)
|
||||
raise JSONRPCError(f'Incorrect JSON data. Missing {e}')
|
||||
|
||||
log.debug('setup complete, now handling method:%s rpcid:%s',
|
||||
request.rpc_method, request.rpc_id, )
|
||||
|
|
@ -561,8 +560,7 @@ def includeme(config):
|
|||
config.add_view_predicate('jsonrpc_method', MethodPredicate)
|
||||
config.add_view_predicate('jsonrpc_method_not_found', NotFoundPredicate)
|
||||
|
||||
config.add_renderer(DEFAULT_RENDERER, ExtJsonRenderer(
|
||||
serializer=json.dumps, indent=4))
|
||||
config.add_renderer(DEFAULT_RENDERER, ExtJsonRenderer())
|
||||
config.add_directive('add_jsonrpc_method', add_jsonrpc_method)
|
||||
|
||||
config.add_route_predicate(
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class TestApi(object):
|
|||
|
||||
def test_api_missing_non_optional_param_args_null(self):
|
||||
id_, params = build_data(self.apikey, 'get_repo')
|
||||
params = params.replace('"args": {}', '"args": null')
|
||||
params = params.replace(b'"args": {}', b'"args": null')
|
||||
response = api_call(self.app, params)
|
||||
|
||||
expected = 'Missing non optional `repoid` arg in JSON DATA'
|
||||
|
|
@ -71,7 +71,7 @@ class TestApi(object):
|
|||
|
||||
def test_api_missing_non_optional_param_args_bad(self):
|
||||
id_, params = build_data(self.apikey, 'get_repo')
|
||||
params = params.replace('"args": {}', '"args": 1')
|
||||
params = params.replace(b'"args": {}', b'"args": 1')
|
||||
response = api_call(self.app, params)
|
||||
|
||||
expected = 'Missing non optional `repoid` arg in JSON DATA'
|
||||
|
|
@ -111,13 +111,13 @@ class TestApi(object):
|
|||
|
||||
def test_api_args_is_null(self):
|
||||
__, params = build_data(self.apikey, 'get_users', )
|
||||
params = params.replace('"args": {}', '"args": null')
|
||||
params = params.replace(b'"args": {}', b'"args": null')
|
||||
response = api_call(self.app, params)
|
||||
assert response.status == '200 OK'
|
||||
|
||||
def test_api_args_is_bad(self):
|
||||
__, params = build_data(self.apikey, 'get_users', )
|
||||
params = params.replace('"args": {}', '"args": 1')
|
||||
params = params.replace(b'"args": {}', b'"args": 1')
|
||||
response = api_call(self.app, params)
|
||||
assert response.status == '200 OK'
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,8 @@ def build_data(apikey, method, **kw):
|
|||
|
||||
def api_call(app, params, status=None):
|
||||
response = app.post(
|
||||
API_URL, content_type='application/json', params=params, status=status)
|
||||
API_URL, content_type='application/json', params=params, status=status,
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -470,6 +470,8 @@ def get_repo_nodes(request, apiuser, repoid, revision, root_path,
|
|||
|
||||
ret_type = Optional.extract(ret_type)
|
||||
details = Optional.extract(details)
|
||||
max_file_bytes = Optional.extract(max_file_bytes)
|
||||
|
||||
_extended_types = ['basic', 'full']
|
||||
if details not in _extended_types:
|
||||
raise JSONRPCError('ret_type must be one of %s' % (','.join(_extended_types)))
|
||||
|
|
@ -492,6 +494,7 @@ def get_repo_nodes(request, apiuser, repoid, revision, root_path,
|
|||
repo, revision, root_path, flat=False,
|
||||
extended_info=extended_info, content=content,
|
||||
max_file_bytes=max_file_bytes)
|
||||
|
||||
_map = {
|
||||
'all': _d + _f,
|
||||
'files': _f,
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ def get_method(request, apiuser, pattern=Optional('*')):
|
|||
|
||||
argspec = inspect.getargspec(func)
|
||||
arglist = argspec[0]
|
||||
defaults = map(repr, argspec[3] or [])
|
||||
defaults = list(map(repr, argspec[3] or []))
|
||||
|
||||
default_empty = '<RequiredType>'
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ def test(request, apiuser, args):
|
|||
@jsonrpc_method()
|
||||
def test_ok(request, apiuser):
|
||||
return {
|
||||
'who': u'hello {} '.format(apiuser),
|
||||
'who': f'hello {apiuser}',
|
||||
'obj': {
|
||||
'time': time.time(),
|
||||
'dt': datetime.datetime.now(),
|
||||
|
|
@ -55,12 +55,15 @@ def test_error(request, apiuser):
|
|||
|
||||
@jsonrpc_method()
|
||||
def test_exception(request, apiuser):
|
||||
raise Exception('something unhanddled')
|
||||
raise Exception('something unhandled')
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
def test_params(request, apiuser, params):
|
||||
return u'hello apiuser:{} params:{}'.format(apiuser, params)
|
||||
return {
|
||||
'who': f'hello {apiuser}',
|
||||
'params': params
|
||||
}
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
|
|
@ -69,16 +72,20 @@ def test_params_opt(
|
|||
opt3=Optional(OAttr('apiuser'))):
|
||||
opt2 = Optional.extract(opt2)
|
||||
opt3 = Optional.extract(opt3, evaluate_locals=locals())
|
||||
|
||||
return u'hello apiuser:{} params:{}, opt:[{},{},{}]'.format(
|
||||
apiuser, params, opt1, opt2, opt3)
|
||||
return {
|
||||
'who': f'hello {apiuser}',
|
||||
'params': params,
|
||||
'opts': [
|
||||
opt1, opt2, opt3
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
@jsonrpc_deprecated_method(
|
||||
use_method='test_ok', deprecated_at_version='4.0.0')
|
||||
def test_deprecated_method(request, apiuser):
|
||||
return u'value'
|
||||
return 'value'
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue