comparison rhodecode/controllers/api/__init__.py @ 1793:631caf880b87 beta

implements #329 - api function for adding a users_group into repo - fixed found issues on API with missing params detection - updated docs for api
author Marcin Kuzminski <marcin@python-works.com>
date Wed, 21 Dec 2011 01:53:01 +0200
parents fee9895fa46e
children 6274adc06988
comparison
equal deleted inserted replaced
1792:2afa6b8c2ade 1793:631caf880b87
44 from rhodecode.model.db import User 44 from rhodecode.model.db import User
45 from rhodecode.lib.auth import AuthUser 45 from rhodecode.lib.auth import AuthUser
46 46
47 log = logging.getLogger('JSONRPC') 47 log = logging.getLogger('JSONRPC')
48 48
49
49 class JSONRPCError(BaseException): 50 class JSONRPCError(BaseException):
50 51
51 def __init__(self, message): 52 def __init__(self, message):
52 self.message = message 53 self.message = message
53 super(JSONRPCError, self).__init__() 54 super(JSONRPCError, self).__init__()
65 status=code, 66 status=code,
66 content_type='application/json') 67 content_type='application/json')
67 return resp 68 return resp
68 69
69 70
70
71 class JSONRPCController(WSGIController): 71 class JSONRPCController(WSGIController):
72 """ 72 """
73 A WSGI-speaking JSON-RPC controller class 73 A WSGI-speaking JSON-RPC controller class
74 74
75 See the specification: 75 See the specification:
76 <http://json-rpc.org/wiki/specification>`. 76 <http://json-rpc.org/wiki/specification>`.
77 77
78 Valid controller return values should be json-serializable objects. 78 Valid controller return values should be json-serializable objects.
79 79
80 Sub-classes should catch their exceptions and raise JSONRPCError 80 Sub-classes should catch their exceptions and raise JSONRPCError
81 if they want to pass meaningful errors to the client. 81 if they want to pass meaningful errors to the client.
82 82
83 """ 83 """
84 84
85 def _get_method_args(self): 85 def _get_method_args(self):
86 """ 86 """
87 Return `self._rpc_args` to dispatched controller method 87 Return `self._rpc_args` to dispatched controller method
109 raw_body = environ['wsgi.input'].read(length) 109 raw_body = environ['wsgi.input'].read(length)
110 110
111 try: 111 try:
112 json_body = json.loads(urllib.unquote_plus(raw_body)) 112 json_body = json.loads(urllib.unquote_plus(raw_body))
113 except ValueError, e: 113 except ValueError, e:
114 #catch JSON errors Here 114 # catch JSON errors Here
115 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \ 115 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
116 % (e, urllib.unquote_plus(raw_body))) 116 % (e, urllib.unquote_plus(raw_body)))
117 117
118 # check AUTH based on API KEY 118 # check AUTH based on API KEY
119 try: 119 try:
120 self._req_api_key = json_body['api_key'] 120 self._req_api_key = json_body['api_key']
121 self._req_id = json_body['id'] 121 self._req_id = json_body['id']
122 self._req_method = json_body['method'] 122 self._req_method = json_body['method']
123 self._req_params = json_body['args'] 123 self._request_params = json_body['args']
124 log.debug('method: %s, params: %s', 124 log.debug('method: %s, params: %s',
125 self._req_method, 125 self._req_method,
126 self._req_params) 126 self._request_params)
127 except KeyError, e: 127 except KeyError, e:
128 return jsonrpc_error(message='Incorrect JSON query missing %s' % e) 128 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
129 129
130 # check if we can find this session using api_key 130 # check if we can find this session using api_key
131 try: 131 try:
144 144
145 # now that we have a method, add self._req_params to 145 # now that we have a method, add self._req_params to
146 # self.kargs and dispatch control to WGIController 146 # self.kargs and dispatch control to WGIController
147 argspec = inspect.getargspec(self._func) 147 argspec = inspect.getargspec(self._func)
148 arglist = argspec[0][1:] 148 arglist = argspec[0][1:]
149 defaults = argspec[3] or [] 149 defaults = map(type, argspec[3] or [])
150 default_empty = types.NotImplementedType 150 default_empty = types.NotImplementedType
151 151
152 kwarglist = list(izip_longest(reversed(arglist), reversed(defaults), 152 # kw arguments required by this method
153 fillvalue=default_empty)) 153 func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults),
154 154 fillvalue=default_empty))
155 # this is little trick to inject logged in user for 155
156 # this is little trick to inject logged in user for
156 # perms decorators to work they expect the controller class to have 157 # perms decorators to work they expect the controller class to have
157 # rhodecode_user attribute set 158 # rhodecode_user attribute set
158 self.rhodecode_user = auth_u 159 self.rhodecode_user = auth_u
159 160
160 # This attribute will need to be first param of a method that uses 161 # This attribute will need to be first param of a method that uses
165 return jsonrpc_error(message='This method [%s] does not support ' 166 return jsonrpc_error(message='This method [%s] does not support '
166 'authentication (missing %s param)' % 167 'authentication (missing %s param)' %
167 (self._func.__name__, USER_SESSION_ATTR)) 168 (self._func.__name__, USER_SESSION_ATTR))
168 169
169 # get our arglist and check if we provided them as args 170 # get our arglist and check if we provided them as args
170 for arg, default in kwarglist: 171 for arg, default in func_kwargs.iteritems():
171 if arg == USER_SESSION_ATTR: 172 if arg == USER_SESSION_ATTR:
172 # USER_SESSION_ATTR is something translated from api key and 173 # USER_SESSION_ATTR is something translated from api key and
173 # this is checked before so we don't need validate it 174 # this is checked before so we don't need validate it
174 continue 175 continue
175 176
176 # skip the required param check if it's default value is 177 # skip the required param check if it's default value is
177 # NotImplementedType (default_empty) 178 # NotImplementedType (default_empty)
178 if not self._req_params or (type(default) == default_empty 179 if (default == default_empty and arg not in self._request_params):
179 and arg not in self._req_params): 180 return jsonrpc_error(
180 return jsonrpc_error(message=('Missing non optional %s arg ' 181 message=(
181 'in JSON DATA') % arg) 182 'Missing non optional `%s` arg in JSON DATA' % arg
182 183 )
183 self._rpc_args = {USER_SESSION_ATTR:u} 184 )
184 self._rpc_args.update(self._req_params) 185
186 self._rpc_args = {USER_SESSION_ATTR: u}
187 self._rpc_args.update(self._request_params)
185 188
186 self._rpc_args['action'] = self._req_method 189 self._rpc_args['action'] = self._req_method
187 self._rpc_args['environ'] = environ 190 self._rpc_args['environ'] = environ
188 self._rpc_args['start_response'] = start_response 191 self._rpc_args['start_response'] = start_response
189 192
190 status = [] 193 status = []
191 headers = [] 194 headers = []
192 exc_info = [] 195 exc_info = []
196
193 def change_content(new_status, new_headers, new_exc_info=None): 197 def change_content(new_status, new_headers, new_exc_info=None):
194 status.append(new_status) 198 status.append(new_status)
195 headers.extend(new_headers) 199 headers.extend(new_headers)
196 exc_info.append(new_exc_info) 200 exc_info.append(new_exc_info)
197 201