api: fixes and changes to always return content type in API

- few tweeks here and there
This commit is contained in:
RhodeCode Admin 2023-03-28 10:21:23 +02:00
parent 745c3af99f
commit 047254866b
6 changed files with 67 additions and 58 deletions

View file

@ -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(

View file

@ -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'

View file

@ -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

View file

@ -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,

View file

@ -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>'

View file

@ -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()