##// END OF EJS Templates
version bump
version bump

File last commit:

r2776:63e58ef8 merge default
r3032:133b117e beta
Show More
__init__.py
282 lines | 9.7 KiB | text/x-python | PythonLexer
Beginning of API implementation for rhodecode
r1445 # -*- coding: utf-8 -*-
"""
rhodecode.controllers.api
~~~~~~~~~~~~~~~~~~~~~~~~~
JSON RPC controller
fixed RPC call for api that was missing request id
r1796
Beginning of API implementation for rhodecode
r1445 :created_on: Aug 20, 2011
:author: marcink
2012 copyrights
r1824 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
Beginning of API implementation for rhodecode
r1445 :license: GPLv3, see COPYING for more details.
"""
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2
# of the License or (at your opinion) any later version of the license.
auto white-space removal
r1818 #
Beginning of API implementation for rhodecode
r1445 # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
auto white-space removal
r1818 #
Beginning of API implementation for rhodecode
r1445 # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import inspect
import logging
import types
import urllib
API added checks for a valid repository on pull command...
r1508 import traceback
added extra logging into API calls
r2656 import time
fixed issues with python2.5...
r1514
from rhodecode.lib.compat import izip_longest, json
Beginning of API implementation for rhodecode
r1445
from paste.response import replace_header
from pylons.controllers import WSGIController
API returns proper JSON response
r1661
Beginning of API implementation for rhodecode
r1445
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
HTTPBadRequest, HTTPError
API fixes...
r1489 from rhodecode.model.db import User
Beginning of API implementation for rhodecode
r1445 from rhodecode.lib.auth import AuthUser
added extra logging into API calls
r2656 from rhodecode.lib.base import _get_ip_addr, _get_access_path
from rhodecode.lib.utils2 import safe_unicode
Beginning of API implementation for rhodecode
r1445
log = logging.getLogger('JSONRPC')
implements #329...
r1793
Beginning of API implementation for rhodecode
r1445 class JSONRPCError(BaseException):
def __init__(self, message):
self.message = message
changed API to match fully JSON-RPC specs
r1708 super(JSONRPCError, self).__init__()
Beginning of API implementation for rhodecode
r1445
def __str__(self):
return str(self.message)
created rhodecode-api binary script for working with api via cli...
r2379 def jsonrpc_error(message, retid=None, code=None):
API returns proper JSON response
r1661 """
Generate a Response object with a JSON-RPC error body
"""
from pylons.controllers.util import Response
created rhodecode-api binary script for working with api via cli...
r2379 return Response(
body=json.dumps(dict(id=retid, result=None, error=message)),
status=code,
content_type='application/json'
)
API returns proper JSON response
r1661
Beginning of API implementation for rhodecode
r1445
class JSONRPCController(WSGIController):
"""
A WSGI-speaking JSON-RPC controller class
implements #329...
r1793
Beginning of API implementation for rhodecode
r1445 See the specification:
<http://json-rpc.org/wiki/specification>`.
implements #329...
r1793
Beginning of API implementation for rhodecode
r1445 Valid controller return values should be json-serializable objects.
implements #329...
r1793
Beginning of API implementation for rhodecode
r1445 Sub-classes should catch their exceptions and raise JSONRPCError
if they want to pass meaningful errors to the client.
implements #329...
r1793
Beginning of API implementation for rhodecode
r1445 """
def _get_method_args(self):
"""
Return `self._rpc_args` to dispatched controller method
chosen by __call__
"""
return self._rpc_args
def __call__(self, environ, start_response):
"""
Parse the request body as JSON, look up the method on the
controller and if it exists, dispatch to it.
"""
added extra logging into API calls
r2656 start = time.time()
created rhodecode-api binary script for working with api via cli...
r2379 self._req_id = None
Beginning of API implementation for rhodecode
r1445 if 'CONTENT_LENGTH' not in environ:
log.debug("No Content-Length")
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(retid=self._req_id,
message="No Content-Length in request")
Beginning of API implementation for rhodecode
r1445 else:
length = environ['CONTENT_LENGTH'] or 0
length = int(environ['CONTENT_LENGTH'])
garden...
r1976 log.debug('Content-Length: %s' % length)
Beginning of API implementation for rhodecode
r1445
if length == 0:
log.debug("Content-Length is 0")
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(retid=self._req_id,
message="Content-Length is 0")
Beginning of API implementation for rhodecode
r1445
raw_body = environ['wsgi.input'].read(length)
try:
json_body = json.loads(urllib.unquote_plus(raw_body))
fixed issues with python2.5...
r1514 except ValueError, e:
implements #329...
r1793 # catch JSON errors Here
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(retid=self._req_id,
message="JSON parse error ERR:%s RAW:%r" \
Beginning of API implementation for rhodecode
r1445 % (e, urllib.unquote_plus(raw_body)))
fix for api key lookup, reuse same function in user model
r1693 # check AUTH based on API KEY
Beginning of API implementation for rhodecode
r1445 try:
self._req_api_key = json_body['api_key']
changed API to match fully JSON-RPC specs
r1708 self._req_id = json_body['id']
Beginning of API implementation for rhodecode
r1445 self._req_method = json_body['method']
implements #329...
r1793 self._request_params = json_body['args']
garden...
r1976 log.debug(
'method: %s, params: %s' % (self._req_method,
self._request_params)
)
fixed issues with python2.5...
r1514 except KeyError, e:
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(retid=self._req_id,
message='Incorrect JSON query missing %s' % e)
Beginning of API implementation for rhodecode
r1445
fix for api key lookup, reuse same function in user model
r1693 # check if we can find this session using api_key
Beginning of API implementation for rhodecode
r1445 try:
u = User.get_by_api_key(self._req_api_key)
fix for api key lookup, reuse same function in user model
r1693 if u is None:
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(retid=self._req_id,
message='Invalid API KEY')
Beginning of API implementation for rhodecode
r1445 auth_u = AuthUser(u.user_id, self._req_api_key)
fixed issues with python2.5...
r1514 except Exception, e:
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(retid=self._req_id,
message='Invalid API KEY')
Beginning of API implementation for rhodecode
r1445
self._error = None
try:
self._func = self._find_method()
except AttributeError, e:
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(retid=self._req_id,
message=str(e))
Beginning of API implementation for rhodecode
r1445
# now that we have a method, add self._req_params to
# self.kargs and dispatch control to WGIController
Added optional arguments into API
r1504 argspec = inspect.getargspec(self._func)
arglist = argspec[0][1:]
implements #329...
r1793 defaults = map(type, argspec[3] or [])
Added optional arguments into API
r1504 default_empty = types.NotImplementedType
fixed issues with python2.5...
r1514
implements #329...
r1793 # kw arguments required by this method
func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults),
fillvalue=default_empty))
Beginning of API implementation for rhodecode
r1445
implements #329...
r1793 # this is little trick to inject logged in user for
Beginning of API implementation for rhodecode
r1445 # perms decorators to work they expect the controller class to have
Extended API...
r1500 # rhodecode_user attribute set
Beginning of API implementation for rhodecode
r1445 self.rhodecode_user = auth_u
Extended API...
r1500 # This attribute will need to be first param of a method that uses
# api_key, which is translated to instance of user at that name
USER_SESSION_ATTR = 'apiuser'
if USER_SESSION_ATTR not in arglist:
created rhodecode-api binary script for working with api via cli...
r2379 return jsonrpc_error(
retid=self._req_id,
message='This method [%s] does not support '
'authentication (missing %s param)' % (
self._func.__name__, USER_SESSION_ATTR)
)
Beginning of API implementation for rhodecode
r1445
# get our arglist and check if we provided them as args
implements #329...
r1793 for arg, default in func_kwargs.iteritems():
Extended API...
r1500 if arg == USER_SESSION_ATTR:
implements #329...
r1793 # USER_SESSION_ATTR is something translated from api key and
Extended API...
r1500 # this is checked before so we don't need validate it
Beginning of API implementation for rhodecode
r1445 continue
fixed issues with python2.5...
r1514
implements #329...
r1793 # skip the required param check if it's default value is
Added optional arguments into API
r1504 # NotImplementedType (default_empty)
implements #329...
r1793 if (default == default_empty and arg not in self._request_params):
return jsonrpc_error(
created rhodecode-api binary script for working with api via cli...
r2379 retid=self._req_id,
implements #329...
r1793 message=(
'Missing non optional `%s` arg in JSON DATA' % arg
)
)
Beginning of API implementation for rhodecode
r1445
implements #329...
r1793 self._rpc_args = {USER_SESSION_ATTR: u}
self._rpc_args.update(self._request_params)
Beginning of API implementation for rhodecode
r1445
self._rpc_args['action'] = self._req_method
self._rpc_args['environ'] = environ
self._rpc_args['start_response'] = start_response
status = []
headers = []
exc_info = []
implements #329...
r1793
Beginning of API implementation for rhodecode
r1445 def change_content(new_status, new_headers, new_exc_info=None):
status.append(new_status)
headers.extend(new_headers)
exc_info.append(new_exc_info)
output = WSGIController.__call__(self, environ, change_content)
output = list(output)
headers.append(('Content-Length', str(len(output[0]))))
replace_header(headers, 'Content-Type', 'application/json')
start_response(status[0], headers, exc_info[0])
added extra logging into API calls
r2656 log.info('IP: %s Request to %s time: %.3fs' % (
_get_ip_addr(environ),
safe_unicode(_get_access_path(environ)), time.time() - start)
)
Beginning of API implementation for rhodecode
r1445 return output
def _dispatch_call(self):
"""
Implement dispatch interface specified by WSGIController
"""
try:
raw_response = self._inspect_call(self._func)
if isinstance(raw_response, HTTPError):
self._error = str(raw_response)
fixed issues with python2.5...
r1514 except JSONRPCError, e:
Beginning of API implementation for rhodecode
r1445 self._error = str(e)
fixed issues with python2.5...
r1514 except Exception, e:
log.error('Encountered unhandled exception: %s' \
% traceback.format_exc())
Beginning of API implementation for rhodecode
r1445 json_exc = JSONRPCError('Internal server error')
self._error = str(json_exc)
if self._error is not None:
raw_response = None
auto white-space removal
r1818 response = dict(id=self._req_id, result=raw_response,
changed API to match fully JSON-RPC specs
r1708 error=self._error)
Beginning of API implementation for rhodecode
r1445
try:
return json.dumps(response)
except TypeError, e:
#404 API extensions for showing permission for users...
r2151 log.error('API FAILED. Error encoding response: %s' % e)
fixed RPC call for api that was missing request id
r1796 return json.dumps(
dict(
#404 API extensions for showing permission for users...
r2151 id=self._req_id,
fixed RPC call for api that was missing request id
r1796 result=None,
error="Error encoding response"
)
)
Beginning of API implementation for rhodecode
r1445
def _find_method(self):
"""
Return method named by `self._req_method` in controller if able
"""
garden...
r1976 log.debug('Trying to find JSON-RPC method: %s' % self._req_method)
Beginning of API implementation for rhodecode
r1445 if self._req_method.startswith('_'):
raise AttributeError("Method not allowed")
try:
func = getattr(self, self._req_method, None)
except UnicodeEncodeError:
raise AttributeError("Problem decoding unicode in requested "
"method name.")
if isinstance(func, types.MethodType):
return func
else:
raise AttributeError("No such method: %s" % self._req_method)