##// END OF EJS Templates
docs: update API documentation.
marcink -
r1603:a0209c61 default
parent child Browse files
Show More
@@ -1,376 +1,371 b''
1 1 .. _pull-request-methods-ref:
2 2
3 3 pull_request methods
4 4 ====================
5 5
6 6 close_pull_request
7 7 ------------------
8 8
9 9 .. py:function:: close_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
10 10
11 11 Close the pull request specified by `pullrequestid`.
12 12
13 13 :param apiuser: This is filled automatically from the |authtoken|.
14 14 :type apiuser: AuthUser
15 15 :param repoid: Repository name or repository ID to which the pull
16 16 request belongs.
17 17 :type repoid: str or int
18 18 :param pullrequestid: ID of the pull request to be closed.
19 19 :type pullrequestid: int
20 20 :param userid: Close the pull request as this user.
21 21 :type userid: Optional(str or int)
22 22
23 23 Example output:
24 24
25 25 .. code-block:: bash
26 26
27 27 "id": <id_given_in_input>,
28 "result":
29 {
28 "result": {
30 29 "pull_request_id": "<int>",
31 30 "closed": "<bool>"
32 31 },
33 32 "error": null
34 33
35 34
36 35 comment_pull_request
37 36 --------------------
38 37
39 38 .. py:function:: comment_pull_request(apiuser, repoid, pullrequestid, message=<Optional:None>, commit_id=<Optional:None>, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
40 39
41 40 Comment on the pull request specified with the `pullrequestid`,
42 41 in the |repo| specified by the `repoid`, and optionally change the
43 42 review status.
44 43
45 44 :param apiuser: This is filled automatically from the |authtoken|.
46 45 :type apiuser: AuthUser
47 46 :param repoid: The repository name or repository ID.
48 47 :type repoid: str or int
49 48 :param pullrequestid: The pull request ID.
50 49 :type pullrequestid: int
51 50 :param commit_id: Specify the commit_id for which to set a comment. If
52 51 given commit_id is different than latest in the PR status
53 52 change won't be performed.
54 53 :type commit_id: str
55 54 :param message: The text content of the comment.
56 55 :type message: str
57 56 :param status: (**Optional**) Set the approval status of the pull
58 57 request. One of: 'not_reviewed', 'approved', 'rejected',
59 58 'under_review'
60 59 :type status: str
61 60 :param comment_type: Comment type, one of: 'note', 'todo'
62 61 :type comment_type: Optional(str), default: 'note'
63 62 :param userid: Comment on the pull request as this user
64 63 :type userid: Optional(str or int)
65 64
66 65 Example output:
67 66
68 67 .. code-block:: bash
69 68
70 69 id : <id_given_in_input>
71 result :
72 {
70 result : {
73 71 "pull_request_id": "<Integer>",
74 72 "comment_id": "<Integer>",
75 73 "status": {"given": <given_status>,
76 74 "was_changed": <bool status_was_actually_changed> },
77 }
75 },
78 76 error : null
79 77
80 78
81 79 create_pull_request
82 80 -------------------
83 81
84 82 .. py:function:: create_pull_request(apiuser, source_repo, target_repo, source_ref, target_ref, title, description=<Optional:''>, reviewers=<Optional:None>)
85 83
86 84 Creates a new pull request.
87 85
88 86 Accepts refs in the following formats:
89 87
90 88 * branch:<branch_name>:<sha>
91 89 * branch:<branch_name>
92 90 * bookmark:<bookmark_name>:<sha> (Mercurial only)
93 91 * bookmark:<bookmark_name> (Mercurial only)
94 92
95 93 :param apiuser: This is filled automatically from the |authtoken|.
96 94 :type apiuser: AuthUser
97 95 :param source_repo: Set the source repository name.
98 96 :type source_repo: str
99 97 :param target_repo: Set the target repository name.
100 98 :type target_repo: str
101 99 :param source_ref: Set the source ref name.
102 100 :type source_ref: str
103 101 :param target_ref: Set the target ref name.
104 102 :type target_ref: str
105 103 :param title: Set the pull request title.
106 104 :type title: str
107 105 :param description: Set the pull request description.
108 106 :type description: Optional(str)
109 107 :param reviewers: Set the new pull request reviewers list.
110 108 :type reviewers: Optional(list)
111 109 Accepts username strings or objects of the format:
112 {
113 'username': 'nick', 'reasons': ['original author']
114 }
110
111 {'username': 'nick', 'reasons': ['original author']}
115 112
116 113
117 114 get_pull_request
118 115 ----------------
119 116
120 117 .. py:function:: get_pull_request(apiuser, repoid, pullrequestid)
121 118
122 119 Get a pull request based on the given ID.
123 120
124 121 :param apiuser: This is filled automatically from the |authtoken|.
125 122 :type apiuser: AuthUser
126 123 :param repoid: Repository name or repository ID from where the pull
127 124 request was opened.
128 125 :type repoid: str or int
129 126 :param pullrequestid: ID of the requested pull request.
130 127 :type pullrequestid: int
131 128
132 129 Example output:
133 130
134 131 .. code-block:: bash
135 132
136 133 "id": <id_given_in_input>,
137 134 "result":
138 135 {
139 136 "pull_request_id": "<pull_request_id>",
140 137 "url": "<url>",
141 138 "title": "<title>",
142 139 "description": "<description>",
143 140 "status" : "<status>",
144 141 "created_on": "<date_time_created>",
145 142 "updated_on": "<date_time_updated>",
146 143 "commit_ids": [
147 144 ...
148 145 "<commit_id>",
149 146 "<commit_id>",
150 147 ...
151 148 ],
152 149 "review_status": "<review_status>",
153 150 "mergeable": {
154 151 "status": "<bool>",
155 152 "message": "<message>",
156 153 },
157 154 "source": {
158 155 "clone_url": "<clone_url>",
159 156 "repository": "<repository_name>",
160 157 "reference":
161 158 {
162 159 "name": "<name>",
163 160 "type": "<type>",
164 161 "commit_id": "<commit_id>",
165 162 }
166 163 },
167 164 "target": {
168 165 "clone_url": "<clone_url>",
169 166 "repository": "<repository_name>",
170 167 "reference":
171 168 {
172 169 "name": "<name>",
173 170 "type": "<type>",
174 171 "commit_id": "<commit_id>",
175 172 }
176 173 },
177 174 "merge": {
178 175 "clone_url": "<clone_url>",
179 176 "reference":
180 177 {
181 178 "name": "<name>",
182 179 "type": "<type>",
183 180 "commit_id": "<commit_id>",
184 181 }
185 182 },
186 183 "author": <user_obj>,
187 184 "reviewers": [
188 185 ...
189 186 {
190 187 "user": "<user_obj>",
191 188 "review_status": "<review_status>",
192 189 }
193 190 ...
194 191 ]
195 192 },
196 193 "error": null
197 194
198 195
199 196 get_pull_requests
200 197 -----------------
201 198
202 199 .. py:function:: get_pull_requests(apiuser, repoid, status=<Optional:'new'>)
203 200
204 201 Get all pull requests from the repository specified in `repoid`.
205 202
206 203 :param apiuser: This is filled automatically from the |authtoken|.
207 204 :type apiuser: AuthUser
208 205 :param repoid: Repository name or repository ID.
209 206 :type repoid: str or int
210 207 :param status: Only return pull requests with the specified status.
211 208 Valid options are.
212 209 * ``new`` (default)
213 210 * ``open``
214 211 * ``closed``
215 212 :type status: str
216 213
217 214 Example output:
218 215
219 216 .. code-block:: bash
220 217
221 218 "id": <id_given_in_input>,
222 219 "result":
223 220 [
224 221 ...
225 222 {
226 223 "pull_request_id": "<pull_request_id>",
227 224 "url": "<url>",
228 225 "title" : "<title>",
229 226 "description": "<description>",
230 227 "status": "<status>",
231 228 "created_on": "<date_time_created>",
232 229 "updated_on": "<date_time_updated>",
233 230 "commit_ids": [
234 231 ...
235 232 "<commit_id>",
236 233 "<commit_id>",
237 234 ...
238 235 ],
239 236 "review_status": "<review_status>",
240 237 "mergeable": {
241 238 "status": "<bool>",
242 239 "message: "<message>",
243 240 },
244 241 "source": {
245 242 "clone_url": "<clone_url>",
246 243 "reference":
247 244 {
248 245 "name": "<name>",
249 246 "type": "<type>",
250 247 "commit_id": "<commit_id>",
251 248 }
252 249 },
253 250 "target": {
254 251 "clone_url": "<clone_url>",
255 252 "reference":
256 253 {
257 254 "name": "<name>",
258 255 "type": "<type>",
259 256 "commit_id": "<commit_id>",
260 257 }
261 258 },
262 259 "merge": {
263 260 "clone_url": "<clone_url>",
264 261 "reference":
265 262 {
266 263 "name": "<name>",
267 264 "type": "<type>",
268 265 "commit_id": "<commit_id>",
269 266 }
270 267 },
271 268 "author": <user_obj>,
272 269 "reviewers": [
273 270 ...
274 271 {
275 272 "user": "<user_obj>",
276 273 "review_status": "<review_status>",
277 274 }
278 275 ...
279 276 ]
280 277 }
281 278 ...
282 279 ],
283 280 "error": null
284 281
285 282
286 283 merge_pull_request
287 284 ------------------
288 285
289 286 .. py:function:: merge_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
290 287
291 288 Merge the pull request specified by `pullrequestid` into its target
292 289 repository.
293 290
294 291 :param apiuser: This is filled automatically from the |authtoken|.
295 292 :type apiuser: AuthUser
296 293 :param repoid: The Repository name or repository ID of the
297 294 target repository to which the |pr| is to be merged.
298 295 :type repoid: str or int
299 296 :param pullrequestid: ID of the pull request which shall be merged.
300 297 :type pullrequestid: int
301 298 :param userid: Merge the pull request as this user.
302 299 :type userid: Optional(str or int)
303 300
304 301 Example output:
305 302
306 303 .. code-block:: bash
307 304
308 305 "id": <id_given_in_input>,
309 "result":
310 {
306 "result": {
311 307 "executed": "<bool>",
312 308 "failure_reason": "<int>",
313 309 "merge_commit_id": "<merge_commit_id>",
314 310 "possible": "<bool>",
315 311 "merge_ref": {
316 312 "commit_id": "<commit_id>",
317 313 "type": "<type>",
318 314 "name": "<name>"
319 315 }
320 316 },
321 317 "error": null
322 318
323 319
324 320 update_pull_request
325 321 -------------------
326 322
327 323 .. py:function:: update_pull_request(apiuser, repoid, pullrequestid, title=<Optional:''>, description=<Optional:''>, reviewers=<Optional:None>, update_commits=<Optional:None>, close_pull_request=<Optional:None>)
328 324
329 325 Updates a pull request.
330 326
331 327 :param apiuser: This is filled automatically from the |authtoken|.
332 328 :type apiuser: AuthUser
333 329 :param repoid: The repository name or repository ID.
334 330 :type repoid: str or int
335 331 :param pullrequestid: The pull request ID.
336 332 :type pullrequestid: int
337 333 :param title: Set the pull request title.
338 334 :type title: str
339 335 :param description: Update pull request description.
340 336 :type description: Optional(str)
341 337 :param reviewers: Update pull request reviewers list with new value.
342 338 :type reviewers: Optional(list)
343 339 :param update_commits: Trigger update of commits for this pull request
344 340 :type: update_commits: Optional(bool)
345 341 :param close_pull_request: Close this pull request with rejected state
346 342 :type: close_pull_request: Optional(bool)
347 343
348 344 Example output:
349 345
350 346 .. code-block:: bash
351 347
352 348 id : <id_given_in_input>
353 result :
354 {
349 result : {
355 350 "msg": "Updated pull request `63`",
356 351 "pull_request": <pull_request_object>,
357 352 "updated_reviewers": {
358 353 "added": [
359 354 "username"
360 355 ],
361 356 "removed": []
362 357 },
363 358 "updated_commits": {
364 359 "added": [
365 360 "<sha1_hash>"
366 361 ],
367 362 "common": [
368 363 "<sha1_hash>",
369 364 "<sha1_hash>",
370 365 ],
371 366 "removed": []
372 367 }
373 368 }
374 369 error : null
375 370
376 371
@@ -1,158 +1,210 b''
1 1 .. _server-methods-ref:
2 2
3 3 server methods
4 4 ==============
5 5
6 6 cleanup_sessions
7 7 ----------------
8 8
9 9 .. py:function:: cleanup_sessions(apiuser, older_then=<Optional:60>)
10 10
11 11 Triggers a session cleanup action.
12 12
13 13 If the ``older_then`` option is set, only sessions that hasn't been
14 14 accessed in the given number of days will be removed.
15 15
16 16 This command can only be run using an |authtoken| with admin rights to
17 17 the specified repository.
18 18
19 19 This command takes the following options:
20 20
21 21 :param apiuser: This is filled automatically from the |authtoken|.
22 22 :type apiuser: AuthUser
23 23 :param older_then: Deletes session that hasn't been accessed
24 24 in given number of days.
25 25 :type older_then: Optional(int)
26 26
27 27 Example output:
28 28
29 29 .. code-block:: bash
30 30
31 31 id : <id_given_in_input>
32 32 result: {
33 33 "backend": "<type of backend>",
34 34 "sessions_removed": <number_of_removed_sessions>
35 35 }
36 36 error : null
37 37
38 38 Example error output:
39 39
40 40 .. code-block:: bash
41 41
42 42 id : <id_given_in_input>
43 43 result : null
44 44 error : {
45 45 'Error occurred during session cleanup'
46 46 }
47 47
48 48
49 49 get_ip
50 50 ------
51 51
52 52 .. py:function:: get_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
53 53
54 54 Displays the IP Address as seen from the |RCE| server.
55 55
56 56 * This command displays the IP Address, as well as all the defined IP
57 57 addresses for the specified user. If the ``userid`` is not set, the
58 58 data returned is for the user calling the method.
59 59
60 60 This command can only be run using an |authtoken| with admin rights to
61 61 the specified repository.
62 62
63 63 This command takes the following options:
64 64
65 65 :param apiuser: This is filled automatically from |authtoken|.
66 66 :type apiuser: AuthUser
67 67 :param userid: Sets the userid for which associated IP Address data
68 68 is returned.
69 69 :type userid: Optional(str or int)
70 70
71 71 Example output:
72 72
73 73 .. code-block:: bash
74 74
75 75 id : <id_given_in_input>
76 76 result : {
77 77 "server_ip_addr": "<ip_from_clien>",
78 78 "user_ips": [
79 79 {
80 80 "ip_addr": "<ip_with_mask>",
81 81 "ip_range": ["<start_ip>", "<end_ip>"],
82 82 },
83 83 ...
84 84 ]
85 85 }
86 86
87 87
88 get_method
89 ----------
90
91 .. py:function:: get_method(apiuser, pattern=<Optional:'*'>)
92
93 Returns list of all available API methods. By default match pattern
94 os "*" but any other pattern can be specified. eg *comment* will return
95 all methods with comment inside them. If just single method is matched
96 returned data will also include method specification
97
98 This command can only be run using an |authtoken| with admin rights to
99 the specified repository.
100
101 This command takes the following options:
102
103 :param apiuser: This is filled automatically from the |authtoken|.
104 :type apiuser: AuthUser
105 :param pattern: pattern to match method names against
106 :type older_then: Optional("*")
107
108 Example output:
109
110 .. code-block:: bash
111
112 id : <id_given_in_input>
113 "result": [
114 "changeset_comment",
115 "comment_pull_request",
116 "comment_commit"
117 ]
118 error : null
119
120 .. code-block:: bash
121
122 id : <id_given_in_input>
123 "result": [
124 "comment_commit",
125 {
126 "apiuser": "<RequiredType>",
127 "comment_type": "<Optional:u'note'>",
128 "commit_id": "<RequiredType>",
129 "message": "<RequiredType>",
130 "repoid": "<RequiredType>",
131 "request": "<RequiredType>",
132 "resolves_comment_id": "<Optional:None>",
133 "status": "<Optional:None>",
134 "userid": "<Optional:<OptionalAttr:apiuser>>"
135 }
136 ]
137 error : null
138
139
88 140 get_server_info
89 141 ---------------
90 142
91 143 .. py:function:: get_server_info(apiuser)
92 144
93 145 Returns the |RCE| server information.
94 146
95 147 This includes the running version of |RCE| and all installed
96 148 packages. This command takes the following options:
97 149
98 150 :param apiuser: This is filled automatically from the |authtoken|.
99 151 :type apiuser: AuthUser
100 152
101 153 Example output:
102 154
103 155 .. code-block:: bash
104 156
105 157 id : <id_given_in_input>
106 158 result : {
107 159 'modules': [<module name>,...]
108 160 'py_version': <python version>,
109 161 'platform': <platform type>,
110 162 'rhodecode_version': <rhodecode version>
111 163 }
112 164 error : null
113 165
114 166
115 167 rescan_repos
116 168 ------------
117 169
118 170 .. py:function:: rescan_repos(apiuser, remove_obsolete=<Optional:False>)
119 171
120 172 Triggers a rescan of the specified repositories.
121 173
122 174 * If the ``remove_obsolete`` option is set, it also deletes repositories
123 175 that are found in the database but not on the file system, so called
124 176 "clean zombies".
125 177
126 178 This command can only be run using an |authtoken| with admin rights to
127 179 the specified repository.
128 180
129 181 This command takes the following options:
130 182
131 183 :param apiuser: This is filled automatically from the |authtoken|.
132 184 :type apiuser: AuthUser
133 185 :param remove_obsolete: Deletes repositories from the database that
134 186 are not found on the filesystem.
135 187 :type remove_obsolete: Optional(``True`` | ``False``)
136 188
137 189 Example output:
138 190
139 191 .. code-block:: bash
140 192
141 193 id : <id_given_in_input>
142 194 result : {
143 195 'added': [<added repository name>,...]
144 196 'removed': [<removed repository name>,...]
145 197 }
146 198 error : null
147 199
148 200 Example error output:
149 201
150 202 .. code-block:: bash
151 203
152 204 id : <id_given_in_input>
153 205 result : null
154 206 error : {
155 207 'Error occurred during rescan repositories action'
156 208 }
157 209
158 210
@@ -1,297 +1,325 b''
1 1 .. _user-methods-ref:
2 2
3 3 user methods
4 4 ============
5 5
6 6 create_user
7 7 -----------
8 8
9 9 .. py:function:: create_user(apiuser, username, email, password=<Optional:''>, firstname=<Optional:''>, lastname=<Optional:''>, active=<Optional:True>, admin=<Optional:False>, extern_name=<Optional:'rhodecode'>, extern_type=<Optional:'rhodecode'>, force_password_change=<Optional:False>, create_personal_repo_group=<Optional:None>)
10 10
11 11 Creates a new user and returns the new user object.
12 12
13 13 This command can only be run using an |authtoken| with admin rights to
14 14 the specified repository.
15 15
16 16 This command takes the following options:
17 17
18 18 :param apiuser: This is filled automatically from the |authtoken|.
19 19 :type apiuser: AuthUser
20 20 :param username: Set the new username.
21 21 :type username: str or int
22 22 :param email: Set the user email address.
23 23 :type email: str
24 24 :param password: Set the new user password.
25 25 :type password: Optional(str)
26 26 :param firstname: Set the new user firstname.
27 27 :type firstname: Optional(str)
28 28 :param lastname: Set the new user surname.
29 29 :type lastname: Optional(str)
30 30 :param active: Set the user as active.
31 31 :type active: Optional(``True`` | ``False``)
32 32 :param admin: Give the new user admin rights.
33 33 :type admin: Optional(``True`` | ``False``)
34 34 :param extern_name: Set the authentication plugin name.
35 35 Using LDAP this is filled with LDAP UID.
36 36 :type extern_name: Optional(str)
37 37 :param extern_type: Set the new user authentication plugin.
38 38 :type extern_type: Optional(str)
39 39 :param force_password_change: Force the new user to change password
40 40 on next login.
41 41 :type force_password_change: Optional(``True`` | ``False``)
42 42 :param create_personal_repo_group: Create personal repo group for this user
43 43 :type create_personal_repo_group: Optional(``True`` | ``False``)
44
44 45 Example output:
45 46
46 47 .. code-block:: bash
47 48
48 49 id : <id_given_in_input>
49 50 result: {
50 51 "msg" : "created new user `<username>`",
51 52 "user": <user_obj>
52 53 }
53 54 error: null
54 55
55 56 Example error output:
56 57
57 58 .. code-block:: bash
58 59
59 60 id : <id_given_in_input>
60 61 result : null
61 62 error : {
62 63 "user `<username>` already exist"
63 64 or
64 65 "email `<email>` already exist"
65 66 or
66 67 "failed to create user `<username>`"
67 68 }
68 69
69 70
70 71 delete_user
71 72 -----------
72 73
73 74 .. py:function:: delete_user(apiuser, userid)
74 75
75 76 Deletes the specified user from the |RCE| user database.
76 77
77 78 This command can only be run using an |authtoken| with admin rights to
78 79 the specified repository.
79 80
80 81 .. important::
81 82
82 83 Ensure all open pull requests and open code review
83 84 requests to this user are close.
84 85
85 86 Also ensure all repositories, or repository groups owned by this
86 87 user are reassigned before deletion.
87 88
88 89 This command takes the following options:
89 90
90 91 :param apiuser: This is filled automatically from the |authtoken|.
91 92 :type apiuser: AuthUser
92 93 :param userid: Set the user to delete.
93 94 :type userid: str or int
94 95
95 96 Example output:
96 97
97 98 .. code-block:: bash
98 99
99 100 id : <id_given_in_input>
100 101 result: {
101 102 "msg" : "deleted user ID:<userid> <username>",
102 103 "user": null
103 104 }
104 105 error: null
105 106
106 107 Example error output:
107 108
108 109 .. code-block:: bash
109 110
110 111 id : <id_given_in_input>
111 112 result : null
112 113 error : {
113 114 "failed to delete user ID:<userid> <username>"
114 115 }
115 116
116 117
117 118 get_user
118 119 --------
119 120
120 121 .. py:function:: get_user(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
121 122
122 123 Returns the information associated with a username or userid.
123 124
124 125 * If the ``userid`` is not set, this command returns the information
125 126 for the ``userid`` calling the method.
126 127
127 128 .. note::
128 129
129 130 Normal users may only run this command against their ``userid``. For
130 131 full privileges you must run this command using an |authtoken| with
131 132 admin rights.
132 133
133 134 :param apiuser: This is filled automatically from the |authtoken|.
134 135 :type apiuser: AuthUser
135 136 :param userid: Sets the userid for which data will be returned.
136 137 :type userid: Optional(str or int)
137 138
138 139 Example output:
139 140
140 141 .. code-block:: bash
141 142
142 143 {
143 144 "error": null,
144 145 "id": <id>,
145 146 "result": {
146 147 "active": true,
147 148 "admin": false,
148 "api_key": "api-key",
149 149 "api_keys": [ list of keys ],
150 "auth_tokens": [ list of tokens with details ],
150 151 "email": "user@example.com",
151 152 "emails": [
152 153 "user@example.com"
153 154 ],
154 155 "extern_name": "rhodecode",
155 156 "extern_type": "rhodecode",
156 157 "firstname": "username",
157 158 "ip_addresses": [],
158 159 "language": null,
159 160 "last_login": "Timestamp",
161 "last_activity": "Timestamp",
160 162 "lastname": "surnae",
161 163 "permissions": {
162 164 "global": [
163 165 "hg.inherit_default_perms.true",
164 166 "usergroup.read",
165 167 "hg.repogroup.create.false",
166 168 "hg.create.none",
167 169 "hg.password_reset.enabled",
168 170 "hg.extern_activate.manual",
169 171 "hg.create.write_on_repogroup.false",
170 172 "hg.usergroup.create.false",
171 173 "group.none",
172 174 "repository.none",
173 175 "hg.register.none",
174 176 "hg.fork.repository"
175 177 ],
176 178 "repositories": { "username/example": "repository.write"},
177 179 "repositories_groups": { "user-group/repo": "group.none" },
178 180 "user_groups": { "user_group_name": "usergroup.read" }
179 181 },
180 182 "user_id": 32,
181 183 "username": "username"
182 184 }
183 185 }
184 186
185 187
188 get_user_audit_logs
189 -------------------
190
191 .. py:function:: get_user_audit_logs(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
192
193 Fetches all action logs made by the specified user.
194
195 This command takes the following options:
196
197 :param apiuser: This is filled automatically from the |authtoken|.
198 :type apiuser: AuthUser
199 :param userid: Sets the userid whose list of locked |repos| will be
200 displayed.
201 :type userid: Optional(str or int)
202
203 Example output:
204
205 .. code-block:: bash
206
207 id : <id_given_in_input>
208 result : {
209 [action, action,...]
210 }
211 error : null
212
213
186 214 get_user_locks
187 215 --------------
188 216
189 217 .. py:function:: get_user_locks(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
190 218
191 219 Displays all repositories locked by the specified user.
192 220
193 221 * If this command is run by a non-admin user, it returns
194 222 a list of |repos| locked by that user.
195 223
196 224 This command takes the following options:
197 225
198 226 :param apiuser: This is filled automatically from the |authtoken|.
199 227 :type apiuser: AuthUser
200 228 :param userid: Sets the userid whose list of locked |repos| will be
201 229 displayed.
202 230 :type userid: Optional(str or int)
203 231
204 232 Example output:
205 233
206 234 .. code-block:: bash
207 235
208 236 id : <id_given_in_input>
209 237 result : {
210 238 [repo_object, repo_object,...]
211 239 }
212 240 error : null
213 241
214 242
215 243 get_users
216 244 ---------
217 245
218 246 .. py:function:: get_users(apiuser)
219 247
220 248 Lists all users in the |RCE| user database.
221 249
222 250 This command can only be run using an |authtoken| with admin rights to
223 251 the specified repository.
224 252
225 253 This command takes the following options:
226 254
227 255 :param apiuser: This is filled automatically from the |authtoken|.
228 256 :type apiuser: AuthUser
229 257
230 258 Example output:
231 259
232 260 .. code-block:: bash
233 261
234 262 id : <id_given_in_input>
235 263 result: [<user_object>, ...]
236 264 error: null
237 265
238 266
239 267 update_user
240 268 -----------
241 269
242 270 .. py:function:: update_user(apiuser, userid, username=<Optional:None>, email=<Optional:None>, password=<Optional:None>, firstname=<Optional:None>, lastname=<Optional:None>, active=<Optional:None>, admin=<Optional:None>, extern_type=<Optional:None>, extern_name=<Optional:None>)
243 271
244 272 Updates the details for the specified user, if that user exists.
245 273
246 274 This command can only be run using an |authtoken| with admin rights to
247 275 the specified repository.
248 276
249 277 This command takes the following options:
250 278
251 279 :param apiuser: This is filled automatically from |authtoken|.
252 280 :type apiuser: AuthUser
253 281 :param userid: Set the ``userid`` to update.
254 282 :type userid: str or int
255 283 :param username: Set the new username.
256 284 :type username: str or int
257 285 :param email: Set the new email.
258 286 :type email: str
259 287 :param password: Set the new password.
260 288 :type password: Optional(str)
261 289 :param firstname: Set the new first name.
262 290 :type firstname: Optional(str)
263 291 :param lastname: Set the new surname.
264 292 :type lastname: Optional(str)
265 293 :param active: Set the new user as active.
266 294 :type active: Optional(``True`` | ``False``)
267 295 :param admin: Give the user admin rights.
268 296 :type admin: Optional(``True`` | ``False``)
269 297 :param extern_name: Set the authentication plugin user name.
270 298 Using LDAP this is filled with LDAP UID.
271 299 :type extern_name: Optional(str)
272 300 :param extern_type: Set the authentication plugin type.
273 301 :type extern_type: Optional(str)
274 302
275 303
276 304 Example output:
277 305
278 306 .. code-block:: bash
279 307
280 308 id : <id_given_in_input>
281 309 result: {
282 310 "msg" : "updated user ID:<userid> <username>",
283 311 "user": <user_object>,
284 312 }
285 313 error: null
286 314
287 315 Example error output:
288 316
289 317 .. code-block:: bash
290 318
291 319 id : <id_given_in_input>
292 320 result : null
293 321 error : {
294 322 "failed to update user `<username>`"
295 323 }
296 324
297 325
@@ -1,731 +1,725 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import logging
23 23
24 24 from rhodecode.api import jsonrpc_method, JSONRPCError
25 25 from rhodecode.api.utils import (
26 26 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
27 27 get_pull_request_or_error, get_commit_or_error, get_user_or_error,
28 28 validate_repo_permissions, resolve_ref_or_error)
29 29 from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
30 30 from rhodecode.lib.base import vcs_operation_context
31 31 from rhodecode.lib.utils2 import str2bool
32 32 from rhodecode.model.changeset_status import ChangesetStatusModel
33 33 from rhodecode.model.comment import CommentsModel
34 34 from rhodecode.model.db import Session, ChangesetStatus, ChangesetComment
35 35 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
36 36 from rhodecode.model.settings import SettingsModel
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 @jsonrpc_method()
42 42 def get_pull_request(request, apiuser, repoid, pullrequestid):
43 43 """
44 44 Get a pull request based on the given ID.
45 45
46 46 :param apiuser: This is filled automatically from the |authtoken|.
47 47 :type apiuser: AuthUser
48 48 :param repoid: Repository name or repository ID from where the pull
49 49 request was opened.
50 50 :type repoid: str or int
51 51 :param pullrequestid: ID of the requested pull request.
52 52 :type pullrequestid: int
53 53
54 54 Example output:
55 55
56 56 .. code-block:: bash
57 57
58 58 "id": <id_given_in_input>,
59 59 "result":
60 60 {
61 61 "pull_request_id": "<pull_request_id>",
62 62 "url": "<url>",
63 63 "title": "<title>",
64 64 "description": "<description>",
65 65 "status" : "<status>",
66 66 "created_on": "<date_time_created>",
67 67 "updated_on": "<date_time_updated>",
68 68 "commit_ids": [
69 69 ...
70 70 "<commit_id>",
71 71 "<commit_id>",
72 72 ...
73 73 ],
74 74 "review_status": "<review_status>",
75 75 "mergeable": {
76 76 "status": "<bool>",
77 77 "message": "<message>",
78 78 },
79 79 "source": {
80 80 "clone_url": "<clone_url>",
81 81 "repository": "<repository_name>",
82 82 "reference":
83 83 {
84 84 "name": "<name>",
85 85 "type": "<type>",
86 86 "commit_id": "<commit_id>",
87 87 }
88 88 },
89 89 "target": {
90 90 "clone_url": "<clone_url>",
91 91 "repository": "<repository_name>",
92 92 "reference":
93 93 {
94 94 "name": "<name>",
95 95 "type": "<type>",
96 96 "commit_id": "<commit_id>",
97 97 }
98 98 },
99 99 "merge": {
100 100 "clone_url": "<clone_url>",
101 101 "reference":
102 102 {
103 103 "name": "<name>",
104 104 "type": "<type>",
105 105 "commit_id": "<commit_id>",
106 106 }
107 107 },
108 108 "author": <user_obj>,
109 109 "reviewers": [
110 110 ...
111 111 {
112 112 "user": "<user_obj>",
113 113 "review_status": "<review_status>",
114 114 }
115 115 ...
116 116 ]
117 117 },
118 118 "error": null
119 119 """
120 120 get_repo_or_error(repoid)
121 121 pull_request = get_pull_request_or_error(pullrequestid)
122 122 if not PullRequestModel().check_user_read(
123 123 pull_request, apiuser, api=True):
124 124 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
125 125 data = pull_request.get_api_data()
126 126 return data
127 127
128 128
129 129 @jsonrpc_method()
130 130 def get_pull_requests(request, apiuser, repoid, status=Optional('new')):
131 131 """
132 132 Get all pull requests from the repository specified in `repoid`.
133 133
134 134 :param apiuser: This is filled automatically from the |authtoken|.
135 135 :type apiuser: AuthUser
136 136 :param repoid: Repository name or repository ID.
137 137 :type repoid: str or int
138 138 :param status: Only return pull requests with the specified status.
139 139 Valid options are.
140 140 * ``new`` (default)
141 141 * ``open``
142 142 * ``closed``
143 143 :type status: str
144 144
145 145 Example output:
146 146
147 147 .. code-block:: bash
148 148
149 149 "id": <id_given_in_input>,
150 150 "result":
151 151 [
152 152 ...
153 153 {
154 154 "pull_request_id": "<pull_request_id>",
155 155 "url": "<url>",
156 156 "title" : "<title>",
157 157 "description": "<description>",
158 158 "status": "<status>",
159 159 "created_on": "<date_time_created>",
160 160 "updated_on": "<date_time_updated>",
161 161 "commit_ids": [
162 162 ...
163 163 "<commit_id>",
164 164 "<commit_id>",
165 165 ...
166 166 ],
167 167 "review_status": "<review_status>",
168 168 "mergeable": {
169 169 "status": "<bool>",
170 170 "message: "<message>",
171 171 },
172 172 "source": {
173 173 "clone_url": "<clone_url>",
174 174 "reference":
175 175 {
176 176 "name": "<name>",
177 177 "type": "<type>",
178 178 "commit_id": "<commit_id>",
179 179 }
180 180 },
181 181 "target": {
182 182 "clone_url": "<clone_url>",
183 183 "reference":
184 184 {
185 185 "name": "<name>",
186 186 "type": "<type>",
187 187 "commit_id": "<commit_id>",
188 188 }
189 189 },
190 190 "merge": {
191 191 "clone_url": "<clone_url>",
192 192 "reference":
193 193 {
194 194 "name": "<name>",
195 195 "type": "<type>",
196 196 "commit_id": "<commit_id>",
197 197 }
198 198 },
199 199 "author": <user_obj>,
200 200 "reviewers": [
201 201 ...
202 202 {
203 203 "user": "<user_obj>",
204 204 "review_status": "<review_status>",
205 205 }
206 206 ...
207 207 ]
208 208 }
209 209 ...
210 210 ],
211 211 "error": null
212 212
213 213 """
214 214 repo = get_repo_or_error(repoid)
215 215 if not has_superadmin_permission(apiuser):
216 216 _perms = (
217 217 'repository.admin', 'repository.write', 'repository.read',)
218 218 validate_repo_permissions(apiuser, repoid, repo, _perms)
219 219
220 220 status = Optional.extract(status)
221 221 pull_requests = PullRequestModel().get_all(repo, statuses=[status])
222 222 data = [pr.get_api_data() for pr in pull_requests]
223 223 return data
224 224
225 225
226 226 @jsonrpc_method()
227 227 def merge_pull_request(request, apiuser, repoid, pullrequestid,
228 228 userid=Optional(OAttr('apiuser'))):
229 229 """
230 230 Merge the pull request specified by `pullrequestid` into its target
231 231 repository.
232 232
233 233 :param apiuser: This is filled automatically from the |authtoken|.
234 234 :type apiuser: AuthUser
235 235 :param repoid: The Repository name or repository ID of the
236 236 target repository to which the |pr| is to be merged.
237 237 :type repoid: str or int
238 238 :param pullrequestid: ID of the pull request which shall be merged.
239 239 :type pullrequestid: int
240 240 :param userid: Merge the pull request as this user.
241 241 :type userid: Optional(str or int)
242 242
243 243 Example output:
244 244
245 245 .. code-block:: bash
246 246
247 247 "id": <id_given_in_input>,
248 "result":
249 {
248 "result": {
250 249 "executed": "<bool>",
251 250 "failure_reason": "<int>",
252 251 "merge_commit_id": "<merge_commit_id>",
253 252 "possible": "<bool>",
254 253 "merge_ref": {
255 254 "commit_id": "<commit_id>",
256 255 "type": "<type>",
257 256 "name": "<name>"
258 257 }
259 258 },
260 259 "error": null
261
262 260 """
263 261 repo = get_repo_or_error(repoid)
264 262 if not isinstance(userid, Optional):
265 263 if (has_superadmin_permission(apiuser) or
266 264 HasRepoPermissionAnyApi('repository.admin')(
267 265 user=apiuser, repo_name=repo.repo_name)):
268 266 apiuser = get_user_or_error(userid)
269 267 else:
270 268 raise JSONRPCError('userid is not the same as your user')
271 269
272 270 pull_request = get_pull_request_or_error(pullrequestid)
273 271
274 272 check = MergeCheck.validate(pull_request, user=apiuser)
275 273 merge_possible = not check.failed
276 274
277 275 if not merge_possible:
278 276 reasons = ','.join([msg for _e, msg in check.errors])
279 277 raise JSONRPCError(
280 278 'merge not possible for following reasons: {}'.format(reasons))
281 279
282 280 target_repo = pull_request.target_repo
283 281 extras = vcs_operation_context(
284 282 request.environ, repo_name=target_repo.repo_name,
285 283 username=apiuser.username, action='push',
286 284 scm=target_repo.repo_type)
287 285 merge_response = PullRequestModel().merge(
288 286 pull_request, apiuser, extras=extras)
289 287 if merge_response.executed:
290 288 PullRequestModel().close_pull_request(
291 289 pull_request.pull_request_id, apiuser)
292 290
293 291 Session().commit()
294 292
295 293 # In previous versions the merge response directly contained the merge
296 294 # commit id. It is now contained in the merge reference object. To be
297 295 # backwards compatible we have to extract it again.
298 296 merge_response = merge_response._asdict()
299 297 merge_response['merge_commit_id'] = merge_response['merge_ref'].commit_id
300 298
301 299 return merge_response
302 300
303 301
304 302 @jsonrpc_method()
305 303 def close_pull_request(request, apiuser, repoid, pullrequestid,
306 304 userid=Optional(OAttr('apiuser'))):
307 305 """
308 306 Close the pull request specified by `pullrequestid`.
309 307
310 308 :param apiuser: This is filled automatically from the |authtoken|.
311 309 :type apiuser: AuthUser
312 310 :param repoid: Repository name or repository ID to which the pull
313 311 request belongs.
314 312 :type repoid: str or int
315 313 :param pullrequestid: ID of the pull request to be closed.
316 314 :type pullrequestid: int
317 315 :param userid: Close the pull request as this user.
318 316 :type userid: Optional(str or int)
319 317
320 318 Example output:
321 319
322 320 .. code-block:: bash
323 321
324 322 "id": <id_given_in_input>,
325 "result":
326 {
323 "result": {
327 324 "pull_request_id": "<int>",
328 325 "closed": "<bool>"
329 326 },
330 327 "error": null
331 328
332 329 """
333 330 repo = get_repo_or_error(repoid)
334 331 if not isinstance(userid, Optional):
335 332 if (has_superadmin_permission(apiuser) or
336 333 HasRepoPermissionAnyApi('repository.admin')(
337 334 user=apiuser, repo_name=repo.repo_name)):
338 335 apiuser = get_user_or_error(userid)
339 336 else:
340 337 raise JSONRPCError('userid is not the same as your user')
341 338
342 339 pull_request = get_pull_request_or_error(pullrequestid)
343 340 if not PullRequestModel().check_user_update(
344 341 pull_request, apiuser, api=True):
345 342 raise JSONRPCError(
346 343 'pull request `%s` close failed, no permission to close.' % (
347 344 pullrequestid,))
348 345 if pull_request.is_closed():
349 346 raise JSONRPCError(
350 347 'pull request `%s` is already closed' % (pullrequestid,))
351 348
352 349 PullRequestModel().close_pull_request(
353 350 pull_request.pull_request_id, apiuser)
354 351 Session().commit()
355 352 data = {
356 353 'pull_request_id': pull_request.pull_request_id,
357 354 'closed': True,
358 355 }
359 356 return data
360 357
361 358
362 359 @jsonrpc_method()
363 360 def comment_pull_request(
364 361 request, apiuser, repoid, pullrequestid, message=Optional(None),
365 362 commit_id=Optional(None), status=Optional(None),
366 363 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
367 364 resolves_comment_id=Optional(None),
368 365 userid=Optional(OAttr('apiuser'))):
369 366 """
370 367 Comment on the pull request specified with the `pullrequestid`,
371 368 in the |repo| specified by the `repoid`, and optionally change the
372 369 review status.
373 370
374 371 :param apiuser: This is filled automatically from the |authtoken|.
375 372 :type apiuser: AuthUser
376 373 :param repoid: The repository name or repository ID.
377 374 :type repoid: str or int
378 375 :param pullrequestid: The pull request ID.
379 376 :type pullrequestid: int
380 377 :param commit_id: Specify the commit_id for which to set a comment. If
381 378 given commit_id is different than latest in the PR status
382 379 change won't be performed.
383 380 :type commit_id: str
384 381 :param message: The text content of the comment.
385 382 :type message: str
386 383 :param status: (**Optional**) Set the approval status of the pull
387 384 request. One of: 'not_reviewed', 'approved', 'rejected',
388 385 'under_review'
389 386 :type status: str
390 387 :param comment_type: Comment type, one of: 'note', 'todo'
391 388 :type comment_type: Optional(str), default: 'note'
392 389 :param userid: Comment on the pull request as this user
393 390 :type userid: Optional(str or int)
394 391
395 392 Example output:
396 393
397 394 .. code-block:: bash
398 395
399 396 id : <id_given_in_input>
400 result :
401 {
397 result : {
402 398 "pull_request_id": "<Integer>",
403 399 "comment_id": "<Integer>",
404 400 "status": {"given": <given_status>,
405 401 "was_changed": <bool status_was_actually_changed> },
406 }
402 },
407 403 error : null
408 404 """
409 405 repo = get_repo_or_error(repoid)
410 406 if not isinstance(userid, Optional):
411 407 if (has_superadmin_permission(apiuser) or
412 408 HasRepoPermissionAnyApi('repository.admin')(
413 409 user=apiuser, repo_name=repo.repo_name)):
414 410 apiuser = get_user_or_error(userid)
415 411 else:
416 412 raise JSONRPCError('userid is not the same as your user')
417 413
418 414 pull_request = get_pull_request_or_error(pullrequestid)
419 415 if not PullRequestModel().check_user_read(
420 416 pull_request, apiuser, api=True):
421 417 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
422 418 message = Optional.extract(message)
423 419 status = Optional.extract(status)
424 420 commit_id = Optional.extract(commit_id)
425 421 comment_type = Optional.extract(comment_type)
426 422 resolves_comment_id = Optional.extract(resolves_comment_id)
427 423
428 424 if not message and not status:
429 425 raise JSONRPCError(
430 426 'Both message and status parameters are missing. '
431 427 'At least one is required.')
432 428
433 429 if (status not in (st[0] for st in ChangesetStatus.STATUSES) and
434 430 status is not None):
435 431 raise JSONRPCError('Unknown comment status: `%s`' % status)
436 432
437 433 if commit_id and commit_id not in pull_request.revisions:
438 434 raise JSONRPCError(
439 435 'Invalid commit_id `%s` for this pull request.' % commit_id)
440 436
441 437 allowed_to_change_status = PullRequestModel().check_user_change_status(
442 438 pull_request, apiuser)
443 439
444 440 # if commit_id is passed re-validated if user is allowed to change status
445 441 # based on latest commit_id from the PR
446 442 if commit_id:
447 443 commit_idx = pull_request.revisions.index(commit_id)
448 444 if commit_idx != 0:
449 445 allowed_to_change_status = False
450 446
451 447 if resolves_comment_id:
452 448 comment = ChangesetComment.get(resolves_comment_id)
453 449 if not comment:
454 450 raise JSONRPCError(
455 451 'Invalid resolves_comment_id `%s` for this pull request.'
456 452 % resolves_comment_id)
457 453 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
458 454 raise JSONRPCError(
459 455 'Comment `%s` is wrong type for setting status to resolved.'
460 456 % resolves_comment_id)
461 457
462 458 text = message
463 459 status_label = ChangesetStatus.get_status_lbl(status)
464 460 if status and allowed_to_change_status:
465 461 st_message = ('Status change %(transition_icon)s %(status)s'
466 462 % {'transition_icon': '>', 'status': status_label})
467 463 text = message or st_message
468 464
469 465 rc_config = SettingsModel().get_all_settings()
470 466 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
471 467
472 468 status_change = status and allowed_to_change_status
473 469 comment = CommentsModel().create(
474 470 text=text,
475 471 repo=pull_request.target_repo.repo_id,
476 472 user=apiuser.user_id,
477 473 pull_request=pull_request.pull_request_id,
478 474 f_path=None,
479 475 line_no=None,
480 476 status_change=(status_label if status_change else None),
481 477 status_change_type=(status if status_change else None),
482 478 closing_pr=False,
483 479 renderer=renderer,
484 480 comment_type=comment_type,
485 481 resolves_comment_id=resolves_comment_id
486 482 )
487 483
488 484 if allowed_to_change_status and status:
489 485 ChangesetStatusModel().set_status(
490 486 pull_request.target_repo.repo_id,
491 487 status,
492 488 apiuser.user_id,
493 489 comment,
494 490 pull_request=pull_request.pull_request_id
495 491 )
496 492 Session().flush()
497 493
498 494 Session().commit()
499 495 data = {
500 496 'pull_request_id': pull_request.pull_request_id,
501 497 'comment_id': comment.comment_id if comment else None,
502 498 'status': {'given': status, 'was_changed': status_change},
503 499 }
504 500 return data
505 501
506 502
507 503 @jsonrpc_method()
508 504 def create_pull_request(
509 505 request, apiuser, source_repo, target_repo, source_ref, target_ref,
510 506 title, description=Optional(''), reviewers=Optional(None)):
511 507 """
512 508 Creates a new pull request.
513 509
514 510 Accepts refs in the following formats:
515 511
516 512 * branch:<branch_name>:<sha>
517 513 * branch:<branch_name>
518 514 * bookmark:<bookmark_name>:<sha> (Mercurial only)
519 515 * bookmark:<bookmark_name> (Mercurial only)
520 516
521 517 :param apiuser: This is filled automatically from the |authtoken|.
522 518 :type apiuser: AuthUser
523 519 :param source_repo: Set the source repository name.
524 520 :type source_repo: str
525 521 :param target_repo: Set the target repository name.
526 522 :type target_repo: str
527 523 :param source_ref: Set the source ref name.
528 524 :type source_ref: str
529 525 :param target_ref: Set the target ref name.
530 526 :type target_ref: str
531 527 :param title: Set the pull request title.
532 528 :type title: str
533 529 :param description: Set the pull request description.
534 530 :type description: Optional(str)
535 531 :param reviewers: Set the new pull request reviewers list.
536 532 :type reviewers: Optional(list)
537 533 Accepts username strings or objects of the format:
538 {
539 'username': 'nick', 'reasons': ['original author']
540 }
534
535 {'username': 'nick', 'reasons': ['original author']}
541 536 """
542 537
543 538 source = get_repo_or_error(source_repo)
544 539 target = get_repo_or_error(target_repo)
545 540 if not has_superadmin_permission(apiuser):
546 541 _perms = ('repository.admin', 'repository.write', 'repository.read',)
547 542 validate_repo_permissions(apiuser, source_repo, source, _perms)
548 543
549 544 full_source_ref = resolve_ref_or_error(source_ref, source)
550 545 full_target_ref = resolve_ref_or_error(target_ref, target)
551 546 source_commit = get_commit_or_error(full_source_ref, source)
552 547 target_commit = get_commit_or_error(full_target_ref, target)
553 548 source_scm = source.scm_instance()
554 549 target_scm = target.scm_instance()
555 550
556 551 commit_ranges = target_scm.compare(
557 552 target_commit.raw_id, source_commit.raw_id, source_scm,
558 553 merge=True, pre_load=[])
559 554
560 555 ancestor = target_scm.get_common_ancestor(
561 556 target_commit.raw_id, source_commit.raw_id, source_scm)
562 557
563 558 if not commit_ranges:
564 559 raise JSONRPCError('no commits found')
565 560
566 561 if not ancestor:
567 562 raise JSONRPCError('no common ancestor found')
568 563
569 564 reviewer_objects = Optional.extract(reviewers) or []
570 565 if not isinstance(reviewer_objects, list):
571 566 raise JSONRPCError('reviewers should be specified as a list')
572 567
573 568 reviewers_reasons = []
574 569 for reviewer_object in reviewer_objects:
575 570 reviewer_reasons = []
576 571 if isinstance(reviewer_object, (basestring, int)):
577 572 reviewer_username = reviewer_object
578 573 else:
579 574 reviewer_username = reviewer_object['username']
580 575 reviewer_reasons = reviewer_object.get('reasons', [])
581 576
582 577 user = get_user_or_error(reviewer_username)
583 578 reviewers_reasons.append((user.user_id, reviewer_reasons))
584 579
585 580 pull_request_model = PullRequestModel()
586 581 pull_request = pull_request_model.create(
587 582 created_by=apiuser.user_id,
588 583 source_repo=source_repo,
589 584 source_ref=full_source_ref,
590 585 target_repo=target_repo,
591 586 target_ref=full_target_ref,
592 587 revisions=reversed(
593 588 [commit.raw_id for commit in reversed(commit_ranges)]),
594 589 reviewers=reviewers_reasons,
595 590 title=title,
596 591 description=Optional.extract(description)
597 592 )
598 593
599 594 Session().commit()
600 595 data = {
601 596 'msg': 'Created new pull request `{}`'.format(title),
602 597 'pull_request_id': pull_request.pull_request_id,
603 598 }
604 599 return data
605 600
606 601
607 602 @jsonrpc_method()
608 603 def update_pull_request(
609 604 request, apiuser, repoid, pullrequestid, title=Optional(''),
610 605 description=Optional(''), reviewers=Optional(None),
611 606 update_commits=Optional(None), close_pull_request=Optional(None)):
612 607 """
613 608 Updates a pull request.
614 609
615 610 :param apiuser: This is filled automatically from the |authtoken|.
616 611 :type apiuser: AuthUser
617 612 :param repoid: The repository name or repository ID.
618 613 :type repoid: str or int
619 614 :param pullrequestid: The pull request ID.
620 615 :type pullrequestid: int
621 616 :param title: Set the pull request title.
622 617 :type title: str
623 618 :param description: Update pull request description.
624 619 :type description: Optional(str)
625 620 :param reviewers: Update pull request reviewers list with new value.
626 621 :type reviewers: Optional(list)
627 622 :param update_commits: Trigger update of commits for this pull request
628 623 :type: update_commits: Optional(bool)
629 624 :param close_pull_request: Close this pull request with rejected state
630 625 :type: close_pull_request: Optional(bool)
631 626
632 627 Example output:
633 628
634 629 .. code-block:: bash
635 630
636 631 id : <id_given_in_input>
637 result :
638 {
632 result : {
639 633 "msg": "Updated pull request `63`",
640 634 "pull_request": <pull_request_object>,
641 635 "updated_reviewers": {
642 636 "added": [
643 637 "username"
644 638 ],
645 639 "removed": []
646 640 },
647 641 "updated_commits": {
648 642 "added": [
649 643 "<sha1_hash>"
650 644 ],
651 645 "common": [
652 646 "<sha1_hash>",
653 647 "<sha1_hash>",
654 648 ],
655 649 "removed": []
656 650 }
657 651 }
658 652 error : null
659 653 """
660 654
661 655 repo = get_repo_or_error(repoid)
662 656 pull_request = get_pull_request_or_error(pullrequestid)
663 657 if not PullRequestModel().check_user_update(
664 658 pull_request, apiuser, api=True):
665 659 raise JSONRPCError(
666 660 'pull request `%s` update failed, no permission to update.' % (
667 661 pullrequestid,))
668 662 if pull_request.is_closed():
669 663 raise JSONRPCError(
670 664 'pull request `%s` update failed, pull request is closed' % (
671 665 pullrequestid,))
672 666
673 667 reviewer_objects = Optional.extract(reviewers) or []
674 668 if not isinstance(reviewer_objects, list):
675 669 raise JSONRPCError('reviewers should be specified as a list')
676 670
677 671 reviewers_reasons = []
678 672 reviewer_ids = set()
679 673 for reviewer_object in reviewer_objects:
680 674 reviewer_reasons = []
681 675 if isinstance(reviewer_object, (int, basestring)):
682 676 reviewer_username = reviewer_object
683 677 else:
684 678 reviewer_username = reviewer_object['username']
685 679 reviewer_reasons = reviewer_object.get('reasons', [])
686 680
687 681 user = get_user_or_error(reviewer_username)
688 682 reviewer_ids.add(user.user_id)
689 683 reviewers_reasons.append((user.user_id, reviewer_reasons))
690 684
691 685 title = Optional.extract(title)
692 686 description = Optional.extract(description)
693 687 if title or description:
694 688 PullRequestModel().edit(
695 689 pull_request, title or pull_request.title,
696 690 description or pull_request.description)
697 691 Session().commit()
698 692
699 693 commit_changes = {"added": [], "common": [], "removed": []}
700 694 if str2bool(Optional.extract(update_commits)):
701 695 if PullRequestModel().has_valid_update_type(pull_request):
702 696 update_response = PullRequestModel().update_commits(
703 697 pull_request)
704 698 commit_changes = update_response.changes or commit_changes
705 699 Session().commit()
706 700
707 701 reviewers_changes = {"added": [], "removed": []}
708 702 if reviewer_ids:
709 703 added_reviewers, removed_reviewers = \
710 704 PullRequestModel().update_reviewers(pull_request, reviewers_reasons)
711 705
712 706 reviewers_changes['added'] = sorted(
713 707 [get_user_or_error(n).username for n in added_reviewers])
714 708 reviewers_changes['removed'] = sorted(
715 709 [get_user_or_error(n).username for n in removed_reviewers])
716 710 Session().commit()
717 711
718 712 if str2bool(Optional.extract(close_pull_request)):
719 713 PullRequestModel().close_pull_request_with_comment(
720 714 pull_request, apiuser, repo)
721 715 Session().commit()
722 716
723 717 data = {
724 718 'msg': 'Updated pull request `{}`'.format(
725 719 pull_request.pull_request_id),
726 720 'pull_request': pull_request.get_api_data(),
727 721 'updated_commits': commit_changes,
728 722 'updated_reviewers': reviewers_changes
729 723 }
730 724
731 725 return data
@@ -1,514 +1,515 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
24 24 from rhodecode.api.utils import (
25 25 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
26 26 from rhodecode.lib.auth import AuthUser, PasswordGenerator
27 27 from rhodecode.lib.exceptions import DefaultUserException
28 28 from rhodecode.lib.utils2 import safe_int, str2bool
29 29 from rhodecode.model.db import Session, User, Repository
30 30 from rhodecode.model.user import UserModel
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 @jsonrpc_method()
36 36 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
37 37 """
38 38 Returns the information associated with a username or userid.
39 39
40 40 * If the ``userid`` is not set, this command returns the information
41 41 for the ``userid`` calling the method.
42 42
43 43 .. note::
44 44
45 45 Normal users may only run this command against their ``userid``. For
46 46 full privileges you must run this command using an |authtoken| with
47 47 admin rights.
48 48
49 49 :param apiuser: This is filled automatically from the |authtoken|.
50 50 :type apiuser: AuthUser
51 51 :param userid: Sets the userid for which data will be returned.
52 52 :type userid: Optional(str or int)
53 53
54 54 Example output:
55 55
56 56 .. code-block:: bash
57 57
58 58 {
59 59 "error": null,
60 60 "id": <id>,
61 61 "result": {
62 62 "active": true,
63 63 "admin": false,
64 64 "api_keys": [ list of keys ],
65 65 "auth_tokens": [ list of tokens with details ],
66 66 "email": "user@example.com",
67 67 "emails": [
68 68 "user@example.com"
69 69 ],
70 70 "extern_name": "rhodecode",
71 71 "extern_type": "rhodecode",
72 72 "firstname": "username",
73 73 "ip_addresses": [],
74 74 "language": null,
75 75 "last_login": "Timestamp",
76 76 "last_activity": "Timestamp",
77 77 "lastname": "surnae",
78 78 "permissions": {
79 79 "global": [
80 80 "hg.inherit_default_perms.true",
81 81 "usergroup.read",
82 82 "hg.repogroup.create.false",
83 83 "hg.create.none",
84 84 "hg.password_reset.enabled",
85 85 "hg.extern_activate.manual",
86 86 "hg.create.write_on_repogroup.false",
87 87 "hg.usergroup.create.false",
88 88 "group.none",
89 89 "repository.none",
90 90 "hg.register.none",
91 91 "hg.fork.repository"
92 92 ],
93 93 "repositories": { "username/example": "repository.write"},
94 94 "repositories_groups": { "user-group/repo": "group.none" },
95 95 "user_groups": { "user_group_name": "usergroup.read" }
96 96 },
97 97 "user_id": 32,
98 98 "username": "username"
99 99 }
100 100 }
101 101 """
102 102
103 103 if not has_superadmin_permission(apiuser):
104 104 # make sure normal user does not pass someone else userid,
105 105 # he is not allowed to do that
106 106 if not isinstance(userid, Optional) and userid != apiuser.user_id:
107 107 raise JSONRPCError('userid is not the same as your user')
108 108
109 109 userid = Optional.extract(userid, evaluate_locals=locals())
110 110 userid = getattr(userid, 'user_id', userid)
111 111
112 112 user = get_user_or_error(userid)
113 113 data = user.get_api_data(include_secrets=True)
114 114 data['permissions'] = AuthUser(user_id=user.user_id).permissions
115 115 return data
116 116
117 117
118 118 @jsonrpc_method()
119 119 def get_users(request, apiuser):
120 120 """
121 121 Lists all users in the |RCE| user database.
122 122
123 123 This command can only be run using an |authtoken| with admin rights to
124 124 the specified repository.
125 125
126 126 This command takes the following options:
127 127
128 128 :param apiuser: This is filled automatically from the |authtoken|.
129 129 :type apiuser: AuthUser
130 130
131 131 Example output:
132 132
133 133 .. code-block:: bash
134 134
135 135 id : <id_given_in_input>
136 136 result: [<user_object>, ...]
137 137 error: null
138 138 """
139 139
140 140 if not has_superadmin_permission(apiuser):
141 141 raise JSONRPCForbidden()
142 142
143 143 result = []
144 144 users_list = User.query().order_by(User.username) \
145 145 .filter(User.username != User.DEFAULT_USER) \
146 146 .all()
147 147 for user in users_list:
148 148 result.append(user.get_api_data(include_secrets=True))
149 149 return result
150 150
151 151
152 152 @jsonrpc_method()
153 153 def create_user(request, apiuser, username, email, password=Optional(''),
154 154 firstname=Optional(''), lastname=Optional(''),
155 155 active=Optional(True), admin=Optional(False),
156 156 extern_name=Optional('rhodecode'),
157 157 extern_type=Optional('rhodecode'),
158 158 force_password_change=Optional(False),
159 159 create_personal_repo_group=Optional(None)):
160 160 """
161 161 Creates a new user and returns the new user object.
162 162
163 163 This command can only be run using an |authtoken| with admin rights to
164 164 the specified repository.
165 165
166 166 This command takes the following options:
167 167
168 168 :param apiuser: This is filled automatically from the |authtoken|.
169 169 :type apiuser: AuthUser
170 170 :param username: Set the new username.
171 171 :type username: str or int
172 172 :param email: Set the user email address.
173 173 :type email: str
174 174 :param password: Set the new user password.
175 175 :type password: Optional(str)
176 176 :param firstname: Set the new user firstname.
177 177 :type firstname: Optional(str)
178 178 :param lastname: Set the new user surname.
179 179 :type lastname: Optional(str)
180 180 :param active: Set the user as active.
181 181 :type active: Optional(``True`` | ``False``)
182 182 :param admin: Give the new user admin rights.
183 183 :type admin: Optional(``True`` | ``False``)
184 184 :param extern_name: Set the authentication plugin name.
185 185 Using LDAP this is filled with LDAP UID.
186 186 :type extern_name: Optional(str)
187 187 :param extern_type: Set the new user authentication plugin.
188 188 :type extern_type: Optional(str)
189 189 :param force_password_change: Force the new user to change password
190 190 on next login.
191 191 :type force_password_change: Optional(``True`` | ``False``)
192 192 :param create_personal_repo_group: Create personal repo group for this user
193 193 :type create_personal_repo_group: Optional(``True`` | ``False``)
194
194 195 Example output:
195 196
196 197 .. code-block:: bash
197 198
198 199 id : <id_given_in_input>
199 200 result: {
200 201 "msg" : "created new user `<username>`",
201 202 "user": <user_obj>
202 203 }
203 204 error: null
204 205
205 206 Example error output:
206 207
207 208 .. code-block:: bash
208 209
209 210 id : <id_given_in_input>
210 211 result : null
211 212 error : {
212 213 "user `<username>` already exist"
213 214 or
214 215 "email `<email>` already exist"
215 216 or
216 217 "failed to create user `<username>`"
217 218 }
218 219
219 220 """
220 221 if not has_superadmin_permission(apiuser):
221 222 raise JSONRPCForbidden()
222 223
223 224 if UserModel().get_by_username(username):
224 225 raise JSONRPCError("user `%s` already exist" % (username,))
225 226
226 227 if UserModel().get_by_email(email, case_insensitive=True):
227 228 raise JSONRPCError("email `%s` already exist" % (email,))
228 229
229 230 # generate random password if we actually given the
230 231 # extern_name and it's not rhodecode
231 232 if (not isinstance(extern_name, Optional) and
232 233 Optional.extract(extern_name) != 'rhodecode'):
233 234 # generate temporary password if user is external
234 235 password = PasswordGenerator().gen_password(length=16)
235 236 create_repo_group = Optional.extract(create_personal_repo_group)
236 237 if isinstance(create_repo_group, basestring):
237 238 create_repo_group = str2bool(create_repo_group)
238 239
239 240 try:
240 241 user = UserModel().create_or_update(
241 242 username=Optional.extract(username),
242 243 password=Optional.extract(password),
243 244 email=Optional.extract(email),
244 245 firstname=Optional.extract(firstname),
245 246 lastname=Optional.extract(lastname),
246 247 active=Optional.extract(active),
247 248 admin=Optional.extract(admin),
248 249 extern_type=Optional.extract(extern_type),
249 250 extern_name=Optional.extract(extern_name),
250 251 force_password_change=Optional.extract(force_password_change),
251 252 create_repo_group=create_repo_group
252 253 )
253 254 Session().commit()
254 255 return {
255 256 'msg': 'created new user `%s`' % username,
256 257 'user': user.get_api_data(include_secrets=True)
257 258 }
258 259 except Exception:
259 260 log.exception('Error occurred during creation of user')
260 261 raise JSONRPCError('failed to create user `%s`' % (username,))
261 262
262 263
263 264 @jsonrpc_method()
264 265 def update_user(request, apiuser, userid, username=Optional(None),
265 266 email=Optional(None), password=Optional(None),
266 267 firstname=Optional(None), lastname=Optional(None),
267 268 active=Optional(None), admin=Optional(None),
268 269 extern_type=Optional(None), extern_name=Optional(None), ):
269 270 """
270 271 Updates the details for the specified user, if that user exists.
271 272
272 273 This command can only be run using an |authtoken| with admin rights to
273 274 the specified repository.
274 275
275 276 This command takes the following options:
276 277
277 278 :param apiuser: This is filled automatically from |authtoken|.
278 279 :type apiuser: AuthUser
279 280 :param userid: Set the ``userid`` to update.
280 281 :type userid: str or int
281 282 :param username: Set the new username.
282 283 :type username: str or int
283 284 :param email: Set the new email.
284 285 :type email: str
285 286 :param password: Set the new password.
286 287 :type password: Optional(str)
287 288 :param firstname: Set the new first name.
288 289 :type firstname: Optional(str)
289 290 :param lastname: Set the new surname.
290 291 :type lastname: Optional(str)
291 292 :param active: Set the new user as active.
292 293 :type active: Optional(``True`` | ``False``)
293 294 :param admin: Give the user admin rights.
294 295 :type admin: Optional(``True`` | ``False``)
295 296 :param extern_name: Set the authentication plugin user name.
296 297 Using LDAP this is filled with LDAP UID.
297 298 :type extern_name: Optional(str)
298 299 :param extern_type: Set the authentication plugin type.
299 300 :type extern_type: Optional(str)
300 301
301 302
302 303 Example output:
303 304
304 305 .. code-block:: bash
305 306
306 307 id : <id_given_in_input>
307 308 result: {
308 309 "msg" : "updated user ID:<userid> <username>",
309 310 "user": <user_object>,
310 311 }
311 312 error: null
312 313
313 314 Example error output:
314 315
315 316 .. code-block:: bash
316 317
317 318 id : <id_given_in_input>
318 319 result : null
319 320 error : {
320 321 "failed to update user `<username>`"
321 322 }
322 323
323 324 """
324 325 if not has_superadmin_permission(apiuser):
325 326 raise JSONRPCForbidden()
326 327
327 328 user = get_user_or_error(userid)
328 329
329 330 # only non optional arguments will be stored in updates
330 331 updates = {}
331 332
332 333 try:
333 334
334 335 store_update(updates, username, 'username')
335 336 store_update(updates, password, 'password')
336 337 store_update(updates, email, 'email')
337 338 store_update(updates, firstname, 'name')
338 339 store_update(updates, lastname, 'lastname')
339 340 store_update(updates, active, 'active')
340 341 store_update(updates, admin, 'admin')
341 342 store_update(updates, extern_name, 'extern_name')
342 343 store_update(updates, extern_type, 'extern_type')
343 344
344 345 user = UserModel().update_user(user, **updates)
345 346 Session().commit()
346 347 return {
347 348 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
348 349 'user': user.get_api_data(include_secrets=True)
349 350 }
350 351 except DefaultUserException:
351 352 log.exception("Default user edit exception")
352 353 raise JSONRPCError('editing default user is forbidden')
353 354 except Exception:
354 355 log.exception("Error occurred during update of user")
355 356 raise JSONRPCError('failed to update user `%s`' % (userid,))
356 357
357 358
358 359 @jsonrpc_method()
359 360 def delete_user(request, apiuser, userid):
360 361 """
361 362 Deletes the specified user from the |RCE| user database.
362 363
363 364 This command can only be run using an |authtoken| with admin rights to
364 365 the specified repository.
365 366
366 367 .. important::
367 368
368 369 Ensure all open pull requests and open code review
369 370 requests to this user are close.
370 371
371 372 Also ensure all repositories, or repository groups owned by this
372 373 user are reassigned before deletion.
373 374
374 375 This command takes the following options:
375 376
376 377 :param apiuser: This is filled automatically from the |authtoken|.
377 378 :type apiuser: AuthUser
378 379 :param userid: Set the user to delete.
379 380 :type userid: str or int
380 381
381 382 Example output:
382 383
383 384 .. code-block:: bash
384 385
385 386 id : <id_given_in_input>
386 387 result: {
387 388 "msg" : "deleted user ID:<userid> <username>",
388 389 "user": null
389 390 }
390 391 error: null
391 392
392 393 Example error output:
393 394
394 395 .. code-block:: bash
395 396
396 397 id : <id_given_in_input>
397 398 result : null
398 399 error : {
399 400 "failed to delete user ID:<userid> <username>"
400 401 }
401 402
402 403 """
403 404 if not has_superadmin_permission(apiuser):
404 405 raise JSONRPCForbidden()
405 406
406 407 user = get_user_or_error(userid)
407 408
408 409 try:
409 410 UserModel().delete(userid)
410 411 Session().commit()
411 412 return {
412 413 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
413 414 'user': None
414 415 }
415 416 except Exception:
416 417 log.exception("Error occurred during deleting of user")
417 418 raise JSONRPCError(
418 419 'failed to delete user ID:%s %s' % (user.user_id, user.username))
419 420
420 421
421 422 @jsonrpc_method()
422 423 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
423 424 """
424 425 Displays all repositories locked by the specified user.
425 426
426 427 * If this command is run by a non-admin user, it returns
427 428 a list of |repos| locked by that user.
428 429
429 430 This command takes the following options:
430 431
431 432 :param apiuser: This is filled automatically from the |authtoken|.
432 433 :type apiuser: AuthUser
433 434 :param userid: Sets the userid whose list of locked |repos| will be
434 435 displayed.
435 436 :type userid: Optional(str or int)
436 437
437 438 Example output:
438 439
439 440 .. code-block:: bash
440 441
441 442 id : <id_given_in_input>
442 443 result : {
443 444 [repo_object, repo_object,...]
444 445 }
445 446 error : null
446 447 """
447 448
448 449 include_secrets = False
449 450 if not has_superadmin_permission(apiuser):
450 451 # make sure normal user does not pass someone else userid,
451 452 # he is not allowed to do that
452 453 if not isinstance(userid, Optional) and userid != apiuser.user_id:
453 454 raise JSONRPCError('userid is not the same as your user')
454 455 else:
455 456 include_secrets = True
456 457
457 458 userid = Optional.extract(userid, evaluate_locals=locals())
458 459 userid = getattr(userid, 'user_id', userid)
459 460 user = get_user_or_error(userid)
460 461
461 462 ret = []
462 463
463 464 # show all locks
464 465 for r in Repository.getAll():
465 466 _user_id, _time, _reason = r.locked
466 467 if _user_id and _time:
467 468 _api_data = r.get_api_data(include_secrets=include_secrets)
468 469 # if we use user filter just show the locks for this user
469 470 if safe_int(_user_id) == user.user_id:
470 471 ret.append(_api_data)
471 472
472 473 return ret
473 474
474 475
475 476 @jsonrpc_method()
476 477 def get_user_audit_logs(request, apiuser, userid=Optional(OAttr('apiuser'))):
477 478 """
478 479 Fetches all action logs made by the specified user.
479 480
480 481 This command takes the following options:
481 482
482 483 :param apiuser: This is filled automatically from the |authtoken|.
483 484 :type apiuser: AuthUser
484 485 :param userid: Sets the userid whose list of locked |repos| will be
485 486 displayed.
486 487 :type userid: Optional(str or int)
487 488
488 489 Example output:
489 490
490 491 .. code-block:: bash
491 492
492 493 id : <id_given_in_input>
493 494 result : {
494 495 [action, action,...]
495 496 }
496 497 error : null
497 498 """
498 499
499 500 if not has_superadmin_permission(apiuser):
500 501 # make sure normal user does not pass someone else userid,
501 502 # he is not allowed to do that
502 503 if not isinstance(userid, Optional) and userid != apiuser.user_id:
503 504 raise JSONRPCError('userid is not the same as your user')
504 505
505 506 userid = Optional.extract(userid, evaluate_locals=locals())
506 507 userid = getattr(userid, 'user_id', userid)
507 508 user = get_user_or_error(userid)
508 509
509 510 ret = []
510 511
511 512 # show all user actions
512 513 for entry in UserModel().get_user_log(user, filter_term=None):
513 514 ret.append(entry)
514 515 return ret
General Comments 0
You need to be logged in to leave comments. Login now