##// END OF EJS Templates
changed API to match fully JSON-RPC specs
marcink -
r1708:fee9895f beta
parent child Browse files
Show More
@@ -1,362 +1,365 b''
1 .. _api:
1 .. _api:
2
2
3
3
4 API
4 API
5 ===
5 ===
6
6
7
7
8 Starting from RhodeCode version 1.2 a simple API was implemented.
8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 There's a single schema for calling all api methods. API is implemented
9 There's a single schema for calling all api methods. API is implemented
10 with JSON protocol both ways. An url to send API request in RhodeCode is
10 with JSON protocol both ways. An url to send API request in RhodeCode is
11 <your_server>/_admin/api
11 <your_server>/_admin/api
12
12
13
13
14 All clients need to send JSON data in such format::
14 All clients are required to send JSON-RPC spec JSON data::
15
15
16 {
16 {
17 "id:<id>,
17 "api_key":"<api_key>",
18 "api_key":"<api_key>",
18 "method":"<method_name>",
19 "method":"<method_name>",
19 "args":{"<arg_key>":"<arg_val>"}
20 "args":{"<arg_key>":"<arg_val>"}
20 }
21 }
21
22
22 Example call for autopulling remotes repos using curl::
23 Example call for autopulling remotes repos using curl::
23 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
24 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
24
25
25 Simply provide
26 Simply provide
27 - *id* A value of any type, which is used to match the response with the request that it is replying to.
26 - *api_key* for access and permission validation.
28 - *api_key* for access and permission validation.
27 - *method* is name of method to call
29 - *method* is name of method to call
28 - *args* is an key:value list of arguments to pass to method
30 - *args* is an key:value list of arguments to pass to method
29
31
30 .. note::
32 .. note::
31
33
32 api_key can be found in your user account page
34 api_key can be found in your user account page
33
35
34
36
35 RhodeCode API will return always a JSON formatted answer::
37 RhodeCode API will return always a JSON-RPC response::
36
38
37 {
39 {
40 "id":<id>,
38 "result": "<result>",
41 "result": "<result>",
39 "error": null
42 "error": null
40 }
43 }
41
44
42 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
45 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
43 calling api *error* key from response will contain failure description
46 calling api *error* key from response will contain failure description
44 and result will be null.
47 and result will be null.
45
48
46 API METHODS
49 API METHODS
47 +++++++++++
50 +++++++++++
48
51
49
52
50 pull
53 pull
51 ----
54 ----
52
55
53 Pulls given repo from remote location. Can be used to automatically keep
56 Pulls given repo from remote location. Can be used to automatically keep
54 remote repos up to date. This command can be executed only using api_key
57 remote repos up to date. This command can be executed only using api_key
55 belonging to user with admin rights
58 belonging to user with admin rights
56
59
57 INPUT::
60 INPUT::
58
61
59 api_key : "<api_key>"
62 api_key : "<api_key>"
60 method : "pull"
63 method : "pull"
61 args : {
64 args : {
62 "repo" : "<repo_name>"
65 "repo" : "<repo_name>"
63 }
66 }
64
67
65 OUTPUT::
68 OUTPUT::
66
69
67 result : "Pulled from <repo_name>"
70 result : "Pulled from <repo_name>"
68 error : null
71 error : null
69
72
70
73
71 get_users
74 get_users
72 ---------
75 ---------
73
76
74 Lists all existing users. This command can be executed only using api_key
77 Lists all existing users. This command can be executed only using api_key
75 belonging to user with admin rights.
78 belonging to user with admin rights.
76
79
77 INPUT::
80 INPUT::
78
81
79 api_key : "<api_key>"
82 api_key : "<api_key>"
80 method : "get_users"
83 method : "get_users"
81 args : { }
84 args : { }
82
85
83 OUTPUT::
86 OUTPUT::
84
87
85 result: [
88 result: [
86 {
89 {
87 "id" : "<id>",
90 "id" : "<id>",
88 "username" : "<username>",
91 "username" : "<username>",
89 "firstname": "<firstname>",
92 "firstname": "<firstname>",
90 "lastname" : "<lastname>",
93 "lastname" : "<lastname>",
91 "email" : "<email>",
94 "email" : "<email>",
92 "active" : "<bool>",
95 "active" : "<bool>",
93 "admin" :Β  "<bool>",
96 "admin" :Β  "<bool>",
94 "ldap" : "<ldap_dn>"
97 "ldap" : "<ldap_dn>"
95 },
98 },
96 …
99 …
97 ]
100 ]
98 error: null
101 error: null
99
102
100 create_user
103 create_user
101 -----------
104 -----------
102
105
103 Creates new user in RhodeCode. This command can be executed only using api_key
106 Creates new user in RhodeCode. This command can be executed only using api_key
104 belonging to user with admin rights.
107 belonging to user with admin rights.
105
108
106 INPUT::
109 INPUT::
107
110
108 api_key : "<api_key>"
111 api_key : "<api_key>"
109 method : "create_user"
112 method : "create_user"
110 args : {
113 args : {
111 "username" : "<username>",
114 "username" : "<username>",
112 "password" : "<password>",
115 "password" : "<password>",
113 "firstname" : "<firstname>",
116 "firstname" : "<firstname>",
114 "lastname" : "<lastname>",
117 "lastname" : "<lastname>",
115 "email" : "<useremail>"
118 "email" : "<useremail>"
116 "active" : "<bool> = True",
119 "active" : "<bool> = True",
117 "admin" : "<bool> = False",
120 "admin" : "<bool> = False",
118 "ldap_dn" : "<ldap_dn> = None"
121 "ldap_dn" : "<ldap_dn> = None"
119 }
122 }
120
123
121 OUTPUT::
124 OUTPUT::
122
125
123 result: {
126 result: {
124 "msg" : "created new user <username>"
127 "msg" : "created new user <username>"
125 }
128 }
126 error: null
129 error: null
127
130
128 get_users_groups
131 get_users_groups
129 ----------------
132 ----------------
130
133
131 Lists all existing users groups. This command can be executed only using api_key
134 Lists all existing users groups. This command can be executed only using api_key
132 belonging to user with admin rights.
135 belonging to user with admin rights.
133
136
134 INPUT::
137 INPUT::
135
138
136 api_key : "<api_key>"
139 api_key : "<api_key>"
137 method : "get_users_groups"
140 method : "get_users_groups"
138 args : { }
141 args : { }
139
142
140 OUTPUT::
143 OUTPUT::
141
144
142 result : [
145 result : [
143 {
146 {
144 "id" : "<id>",
147 "id" : "<id>",
145 "name" : "<name>",
148 "name" : "<name>",
146 "active": "<bool>",
149 "active": "<bool>",
147 "members" : [
150 "members" : [
148 {
151 {
149 "id" : "<userid>",
152 "id" : "<userid>",
150 "username" : "<username>",
153 "username" : "<username>",
151 "firstname": "<firstname>",
154 "firstname": "<firstname>",
152 "lastname" : "<lastname>",
155 "lastname" : "<lastname>",
153 "email" : "<email>",
156 "email" : "<email>",
154 "active" : "<bool>",
157 "active" : "<bool>",
155 "admin" :Β  "<bool>",
158 "admin" :Β  "<bool>",
156 "ldap" : "<ldap_dn>"
159 "ldap" : "<ldap_dn>"
157 },
160 },
158 …
161 …
159 ]
162 ]
160 }
163 }
161 ]
164 ]
162 error : null
165 error : null
163
166
164 get_users_group
167 get_users_group
165 ---------------
168 ---------------
166
169
167 Gets an existing users group. This command can be executed only using api_key
170 Gets an existing users group. This command can be executed only using api_key
168 belonging to user with admin rights.
171 belonging to user with admin rights.
169
172
170 INPUT::
173 INPUT::
171
174
172 api_key : "<api_key>"
175 api_key : "<api_key>"
173 method : "get_users_group"
176 method : "get_users_group"
174 args : {
177 args : {
175 "group_name" : "<name>"
178 "group_name" : "<name>"
176 }
179 }
177
180
178 OUTPUT::
181 OUTPUT::
179
182
180 result : None if group not exist
183 result : None if group not exist
181 {
184 {
182 "id" : "<id>",
185 "id" : "<id>",
183 "name" : "<name>",
186 "name" : "<name>",
184 "active": "<bool>",
187 "active": "<bool>",
185 "members" : [
188 "members" : [
186 { "id" : "<userid>",
189 { "id" : "<userid>",
187 "username" : "<username>",
190 "username" : "<username>",
188 "firstname": "<firstname>",
191 "firstname": "<firstname>",
189 "lastname" : "<lastname>",
192 "lastname" : "<lastname>",
190 "email" : "<email>",
193 "email" : "<email>",
191 "active" : "<bool>",
194 "active" : "<bool>",
192 "admin" :Β  "<bool>",
195 "admin" :Β  "<bool>",
193 "ldap" : "<ldap_dn>"
196 "ldap" : "<ldap_dn>"
194 },
197 },
195 …
198 …
196 ]
199 ]
197 }
200 }
198 error : null
201 error : null
199
202
200 create_users_group
203 create_users_group
201 ------------------
204 ------------------
202
205
203 Creates new users group. This command can be executed only using api_key
206 Creates new users group. This command can be executed only using api_key
204 belonging to user with admin rights
207 belonging to user with admin rights
205
208
206 INPUT::
209 INPUT::
207
210
208 api_key : "<api_key>"
211 api_key : "<api_key>"
209 method : "create_users_group"
212 method : "create_users_group"
210 args: {
213 args: {
211 "name": "<name>",
214 "name": "<name>",
212 "active":"<bool> = True"
215 "active":"<bool> = True"
213 }
216 }
214
217
215 OUTPUT::
218 OUTPUT::
216
219
217 result: {
220 result: {
218 "id": "<newusersgroupid>",
221 "id": "<newusersgroupid>",
219 "msg": "created new users group <name>"
222 "msg": "created new users group <name>"
220 }
223 }
221 error: null
224 error: null
222
225
223 add_user_to_users_groups
226 add_user_to_users_groups
224 ------------------------
227 ------------------------
225
228
226 Adds a user to a users group. This command can be executed only using api_key
229 Adds a user to a users group. This command can be executed only using api_key
227 belonging to user with admin rights
230 belonging to user with admin rights
228
231
229 INPUT::
232 INPUT::
230
233
231 api_key : "<api_key>"
234 api_key : "<api_key>"
232 method : "add_user_users_group"
235 method : "add_user_users_group"
233 args: {
236 args: {
234 "group_name" : "<groupname>",
237 "group_name" : "<groupname>",
235 "user_name" : "<username>"
238 "user_name" : "<username>"
236 }
239 }
237
240
238 OUTPUT::
241 OUTPUT::
239
242
240 result: {
243 result: {
241 "id": "<newusersgroupmemberid>",
244 "id": "<newusersgroupmemberid>",
242 "msg": "created new users group member"
245 "msg": "created new users group member"
243 }
246 }
244 error: null
247 error: null
245
248
246 get_repos
249 get_repos
247 ---------
250 ---------
248
251
249 Lists all existing repositories. This command can be executed only using api_key
252 Lists all existing repositories. This command can be executed only using api_key
250 belonging to user with admin rights
253 belonging to user with admin rights
251
254
252 INPUT::
255 INPUT::
253
256
254 api_key : "<api_key>"
257 api_key : "<api_key>"
255 method : "get_repos"
258 method : "get_repos"
256 args: { }
259 args: { }
257
260
258 OUTPUT::
261 OUTPUT::
259
262
260 result: [
263 result: [
261 {
264 {
262 "id" : "<id>",
265 "id" : "<id>",
263 "name" : "<name>"
266 "name" : "<name>"
264 "type" : "<type>",
267 "type" : "<type>",
265 "description" : "<description>"
268 "description" : "<description>"
266 },
269 },
267 …
270 …
268 ]
271 ]
269 error: null
272 error: null
270
273
271 get_repo
274 get_repo
272 --------
275 --------
273
276
274 Gets an existing repository. This command can be executed only using api_key
277 Gets an existing repository. This command can be executed only using api_key
275 belonging to user with admin rights
278 belonging to user with admin rights
276
279
277 INPUT::
280 INPUT::
278
281
279 api_key : "<api_key>"
282 api_key : "<api_key>"
280 method : "get_repo"
283 method : "get_repo"
281 args: {
284 args: {
282 "name" : "<name>"
285 "name" : "<name>"
283 }
286 }
284
287
285 OUTPUT::
288 OUTPUT::
286
289
287 result: None if repository not exist
290 result: None if repository not exist
288 {
291 {
289 "id" : "<id>",
292 "id" : "<id>",
290 "name" : "<name>"
293 "name" : "<name>"
291 "type" : "<type>",
294 "type" : "<type>",
292 "description" : "<description>",
295 "description" : "<description>",
293 "members" : [
296 "members" : [
294 { "id" : "<userid>",
297 { "id" : "<userid>",
295 "username" : "<username>",
298 "username" : "<username>",
296 "firstname": "<firstname>",
299 "firstname": "<firstname>",
297 "lastname" : "<lastname>",
300 "lastname" : "<lastname>",
298 "email" : "<email>",
301 "email" : "<email>",
299 "active" : "<bool>",
302 "active" : "<bool>",
300 "admin" :Β  "<bool>",
303 "admin" :Β  "<bool>",
301 "ldap" : "<ldap_dn>",
304 "ldap" : "<ldap_dn>",
302 "permission" : "repository_(read|write|admin)"
305 "permission" : "repository_(read|write|admin)"
303 },
306 },
304 …
307 …
305 {
308 {
306 "id" : "<usersgroupid>",
309 "id" : "<usersgroupid>",
307 "name" : "<usersgroupname>",
310 "name" : "<usersgroupname>",
308 "active": "<bool>",
311 "active": "<bool>",
309 "permission" : "repository_(read|write|admin)"
312 "permission" : "repository_(read|write|admin)"
310 },
313 },
311 …
314 …
312 ]
315 ]
313 }
316 }
314 error: null
317 error: null
315
318
316 create_repo
319 create_repo
317 -----------
320 -----------
318
321
319 Creates a repository. This command can be executed only using api_key
322 Creates a repository. This command can be executed only using api_key
320 belonging to user with admin rights.
323 belonging to user with admin rights.
321 If repository name contains "/", all needed repository groups will be created.
324 If repository name contains "/", all needed repository groups will be created.
322 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
325 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
323 and create "baz" repository with "bar" as group.
326 and create "baz" repository with "bar" as group.
324
327
325 INPUT::
328 INPUT::
326
329
327 api_key : "<api_key>"
330 api_key : "<api_key>"
328 method : "create_repo"
331 method : "create_repo"
329 args: {
332 args: {
330 "name" : "<name>",
333 "name" : "<name>",
331 "owner_name" : "<ownername>",
334 "owner_name" : "<ownername>",
332 "description" : "<description> = ''",
335 "description" : "<description> = ''",
333 "repo_type" : "<type> = 'hg'",
336 "repo_type" : "<type> = 'hg'",
334 "private" : "<bool> = False"
337 "private" : "<bool> = False"
335 }
338 }
336
339
337 OUTPUT::
340 OUTPUT::
338
341
339 result: None
342 result: None
340 error: null
343 error: null
341
344
342 add_user_to_repo
345 add_user_to_repo
343 ----------------
346 ----------------
344
347
345 Add a user to a repository. This command can be executed only using api_key
348 Add a user to a repository. This command can be executed only using api_key
346 belonging to user with admin rights.
349 belonging to user with admin rights.
347 If "perm" is None, user will be removed from the repository.
350 If "perm" is None, user will be removed from the repository.
348
351
349 INPUT::
352 INPUT::
350
353
351 api_key : "<api_key>"
354 api_key : "<api_key>"
352 method : "add_user_to_repo"
355 method : "add_user_to_repo"
353 args: {
356 args: {
354 "repo_name" : "<reponame>",
357 "repo_name" : "<reponame>",
355 "user_name" : "<username>",
358 "user_name" : "<username>",
356 "perm" : "(None|repository_(read|write|admin))",
359 "perm" : "(None|repository_(read|write|admin))",
357 }
360 }
358
361
359 OUTPUT::
362 OUTPUT::
360
363
361 result: None
364 result: None
362 error: null
365 error: null
@@ -1,250 +1,253 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 JSON RPC controller
6 JSON RPC controller
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import inspect
28 import inspect
29 import logging
29 import logging
30 import types
30 import types
31 import urllib
31 import urllib
32 import traceback
32 import traceback
33
33
34 from rhodecode.lib.compat import izip_longest, json
34 from rhodecode.lib.compat import izip_longest, json
35
35
36 from paste.response import replace_header
36 from paste.response import replace_header
37
37
38 from pylons.controllers import WSGIController
38 from pylons.controllers import WSGIController
39
39
40
40
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
42 HTTPBadRequest, HTTPError
42 HTTPBadRequest, HTTPError
43
43
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 class JSONRPCError(BaseException):
49 class JSONRPCError(BaseException):
50
50
51 def __init__(self, message):
51 def __init__(self, message):
52 self.message = message
52 self.message = message
53 super(JSONRPCError, self).__init__()
53
54
54 def __str__(self):
55 def __str__(self):
55 return str(self.message)
56 return str(self.message)
56
57
57
58
58 def jsonrpc_error(message, code=None):
59 def jsonrpc_error(message, code=None):
59 """
60 """
60 Generate a Response object with a JSON-RPC error body
61 Generate a Response object with a JSON-RPC error body
61 """
62 """
62 from pylons.controllers.util import Response
63 from pylons.controllers.util import Response
63 resp = Response(body=json.dumps(dict(result=None, error=message)),
64 resp = Response(body=json.dumps(dict(id=None, result=None, error=message)),
64 status=code,
65 status=code,
65 content_type='application/json')
66 content_type='application/json')
66 return resp
67 return resp
67
68
68
69
69
70
70 class JSONRPCController(WSGIController):
71 class JSONRPCController(WSGIController):
71 """
72 """
72 A WSGI-speaking JSON-RPC controller class
73 A WSGI-speaking JSON-RPC controller class
73
74
74 See the specification:
75 See the specification:
75 <http://json-rpc.org/wiki/specification>`.
76 <http://json-rpc.org/wiki/specification>`.
76
77
77 Valid controller return values should be json-serializable objects.
78 Valid controller return values should be json-serializable objects.
78
79
79 Sub-classes should catch their exceptions and raise JSONRPCError
80 Sub-classes should catch their exceptions and raise JSONRPCError
80 if they want to pass meaningful errors to the client.
81 if they want to pass meaningful errors to the client.
81
82
82 """
83 """
83
84
84 def _get_method_args(self):
85 def _get_method_args(self):
85 """
86 """
86 Return `self._rpc_args` to dispatched controller method
87 Return `self._rpc_args` to dispatched controller method
87 chosen by __call__
88 chosen by __call__
88 """
89 """
89 return self._rpc_args
90 return self._rpc_args
90
91
91 def __call__(self, environ, start_response):
92 def __call__(self, environ, start_response):
92 """
93 """
93 Parse the request body as JSON, look up the method on the
94 Parse the request body as JSON, look up the method on the
94 controller and if it exists, dispatch to it.
95 controller and if it exists, dispatch to it.
95 """
96 """
96 if 'CONTENT_LENGTH' not in environ:
97 if 'CONTENT_LENGTH' not in environ:
97 log.debug("No Content-Length")
98 log.debug("No Content-Length")
98 return jsonrpc_error(message="No Content-Length in request")
99 return jsonrpc_error(message="No Content-Length in request")
99 else:
100 else:
100 length = environ['CONTENT_LENGTH'] or 0
101 length = environ['CONTENT_LENGTH'] or 0
101 length = int(environ['CONTENT_LENGTH'])
102 length = int(environ['CONTENT_LENGTH'])
102 log.debug('Content-Length: %s', length)
103 log.debug('Content-Length: %s', length)
103
104
104 if length == 0:
105 if length == 0:
105 log.debug("Content-Length is 0")
106 log.debug("Content-Length is 0")
106 return jsonrpc_error(message="Content-Length is 0")
107 return jsonrpc_error(message="Content-Length is 0")
107
108
108 raw_body = environ['wsgi.input'].read(length)
109 raw_body = environ['wsgi.input'].read(length)
109
110
110 try:
111 try:
111 json_body = json.loads(urllib.unquote_plus(raw_body))
112 json_body = json.loads(urllib.unquote_plus(raw_body))
112 except ValueError, e:
113 except ValueError, e:
113 #catch JSON errors Here
114 #catch JSON errors Here
114 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
115 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
115 % (e, urllib.unquote_plus(raw_body)))
116 % (e, urllib.unquote_plus(raw_body)))
116
117
117 # check AUTH based on API KEY
118 # check AUTH based on API KEY
118 try:
119 try:
119 self._req_api_key = json_body['api_key']
120 self._req_api_key = json_body['api_key']
121 self._req_id = json_body['id']
120 self._req_method = json_body['method']
122 self._req_method = json_body['method']
121 self._req_params = json_body['args']
123 self._req_params = json_body['args']
122 log.debug('method: %s, params: %s',
124 log.debug('method: %s, params: %s',
123 self._req_method,
125 self._req_method,
124 self._req_params)
126 self._req_params)
125 except KeyError, e:
127 except KeyError, e:
126 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
128 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
127
129
128 # check if we can find this session using api_key
130 # check if we can find this session using api_key
129 try:
131 try:
130 u = User.get_by_api_key(self._req_api_key)
132 u = User.get_by_api_key(self._req_api_key)
131 if u is None:
133 if u is None:
132 return jsonrpc_error(message='Invalid API KEY')
134 return jsonrpc_error(message='Invalid API KEY')
133 auth_u = AuthUser(u.user_id, self._req_api_key)
135 auth_u = AuthUser(u.user_id, self._req_api_key)
134 except Exception, e:
136 except Exception, e:
135 return jsonrpc_error(message='Invalid API KEY')
137 return jsonrpc_error(message='Invalid API KEY')
136
138
137 self._error = None
139 self._error = None
138 try:
140 try:
139 self._func = self._find_method()
141 self._func = self._find_method()
140 except AttributeError, e:
142 except AttributeError, e:
141 return jsonrpc_error(message=str(e))
143 return jsonrpc_error(message=str(e))
142
144
143 # now that we have a method, add self._req_params to
145 # now that we have a method, add self._req_params to
144 # self.kargs and dispatch control to WGIController
146 # self.kargs and dispatch control to WGIController
145 argspec = inspect.getargspec(self._func)
147 argspec = inspect.getargspec(self._func)
146 arglist = argspec[0][1:]
148 arglist = argspec[0][1:]
147 defaults = argspec[3] or []
149 defaults = argspec[3] or []
148 default_empty = types.NotImplementedType
150 default_empty = types.NotImplementedType
149
151
150 kwarglist = list(izip_longest(reversed(arglist), reversed(defaults),
152 kwarglist = list(izip_longest(reversed(arglist), reversed(defaults),
151 fillvalue=default_empty))
153 fillvalue=default_empty))
152
154
153 # this is little trick to inject logged in user for
155 # this is little trick to inject logged in user for
154 # perms decorators to work they expect the controller class to have
156 # perms decorators to work they expect the controller class to have
155 # rhodecode_user attribute set
157 # rhodecode_user attribute set
156 self.rhodecode_user = auth_u
158 self.rhodecode_user = auth_u
157
159
158 # This attribute will need to be first param of a method that uses
160 # This attribute will need to be first param of a method that uses
159 # api_key, which is translated to instance of user at that name
161 # api_key, which is translated to instance of user at that name
160 USER_SESSION_ATTR = 'apiuser'
162 USER_SESSION_ATTR = 'apiuser'
161
163
162 if USER_SESSION_ATTR not in arglist:
164 if USER_SESSION_ATTR not in arglist:
163 return jsonrpc_error(message='This method [%s] does not support '
165 return jsonrpc_error(message='This method [%s] does not support '
164 'authentication (missing %s param)' %
166 'authentication (missing %s param)' %
165 (self._func.__name__, USER_SESSION_ATTR))
167 (self._func.__name__, USER_SESSION_ATTR))
166
168
167 # get our arglist and check if we provided them as args
169 # get our arglist and check if we provided them as args
168 for arg, default in kwarglist:
170 for arg, default in kwarglist:
169 if arg == USER_SESSION_ATTR:
171 if arg == USER_SESSION_ATTR:
170 # USER_SESSION_ATTR is something translated from api key and
172 # USER_SESSION_ATTR is something translated from api key and
171 # this is checked before so we don't need validate it
173 # this is checked before so we don't need validate it
172 continue
174 continue
173
175
174 # skip the required param check if it's default value is
176 # skip the required param check if it's default value is
175 # NotImplementedType (default_empty)
177 # NotImplementedType (default_empty)
176 if not self._req_params or (type(default) == default_empty
178 if not self._req_params or (type(default) == default_empty
177 and arg not in self._req_params):
179 and arg not in self._req_params):
178 return jsonrpc_error(message=('Missing non optional %s arg '
180 return jsonrpc_error(message=('Missing non optional %s arg '
179 'in JSON DATA') % arg)
181 'in JSON DATA') % arg)
180
182
181 self._rpc_args = {USER_SESSION_ATTR:u}
183 self._rpc_args = {USER_SESSION_ATTR:u}
182 self._rpc_args.update(self._req_params)
184 self._rpc_args.update(self._req_params)
183
185
184 self._rpc_args['action'] = self._req_method
186 self._rpc_args['action'] = self._req_method
185 self._rpc_args['environ'] = environ
187 self._rpc_args['environ'] = environ
186 self._rpc_args['start_response'] = start_response
188 self._rpc_args['start_response'] = start_response
187
189
188 status = []
190 status = []
189 headers = []
191 headers = []
190 exc_info = []
192 exc_info = []
191 def change_content(new_status, new_headers, new_exc_info=None):
193 def change_content(new_status, new_headers, new_exc_info=None):
192 status.append(new_status)
194 status.append(new_status)
193 headers.extend(new_headers)
195 headers.extend(new_headers)
194 exc_info.append(new_exc_info)
196 exc_info.append(new_exc_info)
195
197
196 output = WSGIController.__call__(self, environ, change_content)
198 output = WSGIController.__call__(self, environ, change_content)
197 output = list(output)
199 output = list(output)
198 headers.append(('Content-Length', str(len(output[0]))))
200 headers.append(('Content-Length', str(len(output[0]))))
199 replace_header(headers, 'Content-Type', 'application/json')
201 replace_header(headers, 'Content-Type', 'application/json')
200 start_response(status[0], headers, exc_info[0])
202 start_response(status[0], headers, exc_info[0])
201
203
202 return output
204 return output
203
205
204 def _dispatch_call(self):
206 def _dispatch_call(self):
205 """
207 """
206 Implement dispatch interface specified by WSGIController
208 Implement dispatch interface specified by WSGIController
207 """
209 """
208 try:
210 try:
209 raw_response = self._inspect_call(self._func)
211 raw_response = self._inspect_call(self._func)
210 if isinstance(raw_response, HTTPError):
212 if isinstance(raw_response, HTTPError):
211 self._error = str(raw_response)
213 self._error = str(raw_response)
212 except JSONRPCError, e:
214 except JSONRPCError, e:
213 self._error = str(e)
215 self._error = str(e)
214 except Exception, e:
216 except Exception, e:
215 log.error('Encountered unhandled exception: %s' \
217 log.error('Encountered unhandled exception: %s' \
216 % traceback.format_exc())
218 % traceback.format_exc())
217 json_exc = JSONRPCError('Internal server error')
219 json_exc = JSONRPCError('Internal server error')
218 self._error = str(json_exc)
220 self._error = str(json_exc)
219
221
220 if self._error is not None:
222 if self._error is not None:
221 raw_response = None
223 raw_response = None
222
224
223 response = dict(result=raw_response, error=self._error)
225 response = dict(id=self._req_id, result=raw_response,
226 error=self._error)
224
227
225 try:
228 try:
226 return json.dumps(response)
229 return json.dumps(response)
227 except TypeError, e:
230 except TypeError, e:
228 log.debug('Error encoding response: %s', e)
231 log.debug('Error encoding response: %s', e)
229 return json.dumps(dict(result=None,
232 return json.dumps(dict(result=None,
230 error="Error encoding response"))
233 error="Error encoding response"))
231
234
232 def _find_method(self):
235 def _find_method(self):
233 """
236 """
234 Return method named by `self._req_method` in controller if able
237 Return method named by `self._req_method` in controller if able
235 """
238 """
236 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
239 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
237 if self._req_method.startswith('_'):
240 if self._req_method.startswith('_'):
238 raise AttributeError("Method not allowed")
241 raise AttributeError("Method not allowed")
239
242
240 try:
243 try:
241 func = getattr(self, self._req_method, None)
244 func = getattr(self, self._req_method, None)
242 except UnicodeEncodeError:
245 except UnicodeEncodeError:
243 raise AttributeError("Problem decoding unicode in requested "
246 raise AttributeError("Problem decoding unicode in requested "
244 "method name.")
247 "method name.")
245
248
246 if isinstance(func, types.MethodType):
249 if isinstance(func, types.MethodType):
247 return func
250 return func
248 else:
251 else:
249 raise AttributeError("No such method: %s" % self._req_method)
252 raise AttributeError("No such method: %s" % self._req_method)
250
253
General Comments 0
You need to be logged in to leave comments. Login now