Show More
@@ -0,0 +1,107 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2010-2019 RhodeCode GmbH | |||
|
4 | # | |||
|
5 | # This program is free software: you can redistribute it and/or modify | |||
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU Affero General Public License | |||
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | ||||
|
22 | import pytest | |||
|
23 | ||||
|
24 | from rhodecode.model.db import User, ChangesetComment | |||
|
25 | from rhodecode.model.meta import Session | |||
|
26 | from rhodecode.model.comment import CommentsModel | |||
|
27 | from rhodecode.api.tests.utils import ( | |||
|
28 | build_data, api_call, assert_error, assert_call_ok) | |||
|
29 | ||||
|
30 | ||||
|
31 | @pytest.fixture() | |||
|
32 | def make_repo_comments_factory(request): | |||
|
33 | ||||
|
34 | def maker(repo): | |||
|
35 | user = User.get_first_super_admin() | |||
|
36 | commit = repo.scm_instance()[0] | |||
|
37 | ||||
|
38 | commit_id = commit.raw_id | |||
|
39 | file_0 = commit.affected_files[0] | |||
|
40 | comments = [] | |||
|
41 | ||||
|
42 | # general | |||
|
43 | CommentsModel().create( | |||
|
44 | text='General Comment', repo=repo, user=user, commit_id=commit_id, | |||
|
45 | comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False) | |||
|
46 | ||||
|
47 | # inline | |||
|
48 | CommentsModel().create( | |||
|
49 | text='Inline Comment', repo=repo, user=user, commit_id=commit_id, | |||
|
50 | f_path=file_0, line_no='n1', | |||
|
51 | comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False) | |||
|
52 | ||||
|
53 | # todo | |||
|
54 | CommentsModel().create( | |||
|
55 | text='INLINE TODO Comment', repo=repo, user=user, commit_id=commit_id, | |||
|
56 | f_path=file_0, line_no='n1', | |||
|
57 | comment_type=ChangesetComment.COMMENT_TYPE_TODO, send_email=False) | |||
|
58 | ||||
|
59 | @request.addfinalizer | |||
|
60 | def cleanup(): | |||
|
61 | for comment in comments: | |||
|
62 | Session().delete(comment) | |||
|
63 | return maker | |||
|
64 | ||||
|
65 | ||||
|
66 | @pytest.mark.usefixtures("testuser_api", "app") | |||
|
67 | class TestGetRepo(object): | |||
|
68 | ||||
|
69 | @pytest.mark.parametrize('filters, expected_count', [ | |||
|
70 | ({}, 3), | |||
|
71 | ({'comment_type': ChangesetComment.COMMENT_TYPE_NOTE}, 2), | |||
|
72 | ({'comment_type': ChangesetComment.COMMENT_TYPE_TODO}, 1), | |||
|
73 | ({'commit_id': 'FILLED DYNAMIC'}, 3), | |||
|
74 | ]) | |||
|
75 | def test_api_get_repo_comments(self, backend, user_util, | |||
|
76 | make_repo_comments_factory, filters, expected_count): | |||
|
77 | commits = [{'message': 'A'}, {'message': 'B'}] | |||
|
78 | repo = backend.create_repo(commits=commits) | |||
|
79 | make_repo_comments_factory(repo) | |||
|
80 | ||||
|
81 | api_call_params = {'repoid': repo.repo_name,} | |||
|
82 | api_call_params.update(filters) | |||
|
83 | ||||
|
84 | if 'commit_id' in api_call_params: | |||
|
85 | commit = repo.scm_instance()[0] | |||
|
86 | commit_id = commit.raw_id | |||
|
87 | api_call_params['commit_id'] = commit_id | |||
|
88 | ||||
|
89 | id_, params = build_data(self.apikey, 'get_repo_comments', **api_call_params) | |||
|
90 | response = api_call(self.app, params) | |||
|
91 | result = assert_call_ok(id_, given=response.body) | |||
|
92 | ||||
|
93 | assert len(result) == expected_count | |||
|
94 | ||||
|
95 | def test_api_get_repo_comments_wrong_comment_typ(self, backend_hg): | |||
|
96 | ||||
|
97 | repo = backend_hg.create_repo() | |||
|
98 | make_repo_comments_factory(repo) | |||
|
99 | ||||
|
100 | api_call_params = {'repoid': repo.repo_name,} | |||
|
101 | api_call_params.update({'comment_type': 'bogus'}) | |||
|
102 | ||||
|
103 | expected = 'comment_type must be one of `{}` got {}'.format( | |||
|
104 | ChangesetComment.COMMENT_TYPES, 'bogus') | |||
|
105 | id_, params = build_data(self.apikey, 'get_repo_comments', **api_call_params) | |||
|
106 | response = api_call(self.app, params) | |||
|
107 | assert_error(id_, expected, given=response.body) |
@@ -86,7 +86,9 b' class TestApi(object):' | |||||
86 | def test_api_non_existing_method_have_similar(self, request): |
|
86 | def test_api_non_existing_method_have_similar(self, request): | |
87 | id_, params = build_data(self.apikey, 'comment', args='xx') |
|
87 | id_, params = build_data(self.apikey, 'comment', args='xx') | |
88 | response = api_call(self.app, params) |
|
88 | response = api_call(self.app, params) | |
89 | expected = 'No such method: comment. Similar methods: changeset_comment, comment_pull_request, get_pull_request_comments, comment_commit' |
|
89 | expected = 'No such method: comment. ' \ | |
|
90 | 'Similar methods: changeset_comment, comment_pull_request, ' \ | |||
|
91 | 'get_pull_request_comments, comment_commit, get_repo_comments' | |||
90 | assert_error(id_, expected, given=response.body) |
|
92 | assert_error(id_, expected, given=response.body) | |
91 |
|
93 | |||
92 | def test_api_disabled_user(self, request): |
|
94 | def test_api_disabled_user(self, request): |
@@ -38,7 +38,7 b' class TestGetMethod(object):' | |||||
38 | response = api_call(self.app, params) |
|
38 | response = api_call(self.app, params) | |
39 |
|
39 | |||
40 | expected = ['changeset_comment', 'comment_pull_request', |
|
40 | expected = ['changeset_comment', 'comment_pull_request', | |
41 | 'get_pull_request_comments', 'comment_commit'] |
|
41 | 'get_pull_request_comments', 'comment_commit', 'get_repo_comments'] | |
42 | assert_ok(id_, expected, given=response.body) |
|
42 | assert_ok(id_, expected, given=response.body) | |
43 |
|
43 | |||
44 | def test_get_methods_on_single_match(self): |
|
44 | def test_get_methods_on_single_match(self): |
@@ -59,6 +59,7 b' class TestGetPullRequestComments(object)' | |||||
59 | 'status_lbl': 'Under Review'}, |
|
59 | 'status_lbl': 'Under Review'}, | |
60 | 'comment_text': 'Auto status change to |new_status|\n\n.. |new_status| replace:: *"Under Review"*', |
|
60 | 'comment_text': 'Auto status change to |new_status|\n\n.. |new_status| replace:: *"Under Review"*', | |
61 | 'comment_type': 'note', |
|
61 | 'comment_type': 'note', | |
|
62 | 'comment_resolved_by': None, | |||
62 | 'pull_request_version': None} |
|
63 | 'pull_request_version': None} | |
63 | ] |
|
64 | ] | |
64 | assert_ok(id_, expected, response.body) |
|
65 | assert_ok(id_, expected, response.body) |
@@ -28,6 +28,19 b' from rhodecode.lib.ext_json import json' | |||||
28 | API_URL = '/_admin/api' |
|
28 | API_URL = '/_admin/api' | |
29 |
|
29 | |||
30 |
|
30 | |||
|
31 | def assert_call_ok(id_, given): | |||
|
32 | expected = jsonify({ | |||
|
33 | 'id': id_, | |||
|
34 | 'error': None, | |||
|
35 | 'result': None | |||
|
36 | }) | |||
|
37 | given = json.loads(given) | |||
|
38 | ||||
|
39 | assert expected['id'] == given['id'] | |||
|
40 | assert expected['error'] == given['error'] | |||
|
41 | return given['result'] | |||
|
42 | ||||
|
43 | ||||
31 | def assert_ok(id_, expected, given): |
|
44 | def assert_ok(id_, expected, given): | |
32 | expected = jsonify({ |
|
45 | expected = jsonify({ | |
33 | 'id': id_, |
|
46 | 'id': id_, | |
@@ -55,8 +68,6 b' def jsonify(obj):' | |||||
55 | def build_data(apikey, method, **kw): |
|
68 | def build_data(apikey, method, **kw): | |
56 | """ |
|
69 | """ | |
57 | Builds API data with given random ID |
|
70 | Builds API data with given random ID | |
58 |
|
||||
59 | :param random_id: |
|
|||
60 | """ |
|
71 | """ | |
61 | random_id = random.randrange(1, 9999) |
|
72 | random_id = random.randrange(1, 9999) | |
62 | return random_id, json.dumps({ |
|
73 | return random_id, json.dumps({ |
@@ -1506,6 +1506,73 b' def comment_commit(' | |||||
1506 |
|
1506 | |||
1507 |
|
1507 | |||
1508 | @jsonrpc_method() |
|
1508 | @jsonrpc_method() | |
|
1509 | def get_repo_comments(request, apiuser, repoid, | |||
|
1510 | commit_id=Optional(None), comment_type=Optional(None), | |||
|
1511 | userid=Optional(None)): | |||
|
1512 | """ | |||
|
1513 | Get all comments for a repository | |||
|
1514 | ||||
|
1515 | :param apiuser: This is filled automatically from the |authtoken|. | |||
|
1516 | :type apiuser: AuthUser | |||
|
1517 | :param repoid: Set the repository name or repository ID. | |||
|
1518 | :type repoid: str or int | |||
|
1519 | :param commit_id: Optionally filter the comments by the commit_id | |||
|
1520 | :type commit_id: Optional(str), default: None | |||
|
1521 | :param comment_type: Optionally filter the comments by the comment_type | |||
|
1522 | one of: 'note', 'todo' | |||
|
1523 | :type comment_type: Optional(str), default: None | |||
|
1524 | :param userid: Optionally filter the comments by the author of comment | |||
|
1525 | :type userid: Optional(str or int), Default: None | |||
|
1526 | ||||
|
1527 | Example error output: | |||
|
1528 | ||||
|
1529 | .. code-block:: bash | |||
|
1530 | ||||
|
1531 | { | |||
|
1532 | "id" : <id_given_in_input>, | |||
|
1533 | "result" : [ | |||
|
1534 | { | |||
|
1535 | "comment_author": <USER_DETAILS>, | |||
|
1536 | "comment_created_on": "2017-02-01T14:38:16.309", | |||
|
1537 | "comment_f_path": "file.txt", | |||
|
1538 | "comment_id": 282, | |||
|
1539 | "comment_lineno": "n1", | |||
|
1540 | "comment_resolved_by": null, | |||
|
1541 | "comment_status": [], | |||
|
1542 | "comment_text": "This file needs a header", | |||
|
1543 | "comment_type": "todo" | |||
|
1544 | } | |||
|
1545 | ], | |||
|
1546 | "error" : null | |||
|
1547 | } | |||
|
1548 | ||||
|
1549 | """ | |||
|
1550 | repo = get_repo_or_error(repoid) | |||
|
1551 | if not has_superadmin_permission(apiuser): | |||
|
1552 | _perms = ('repository.read', 'repository.write', 'repository.admin') | |||
|
1553 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |||
|
1554 | ||||
|
1555 | commit_id = Optional.extract(commit_id) | |||
|
1556 | ||||
|
1557 | userid = Optional.extract(userid) | |||
|
1558 | if userid: | |||
|
1559 | user = get_user_or_error(userid) | |||
|
1560 | else: | |||
|
1561 | user = None | |||
|
1562 | ||||
|
1563 | comment_type = Optional.extract(comment_type) | |||
|
1564 | if comment_type and comment_type not in ChangesetComment.COMMENT_TYPES: | |||
|
1565 | raise JSONRPCError( | |||
|
1566 | 'comment_type must be one of `{}` got {}'.format( | |||
|
1567 | ChangesetComment.COMMENT_TYPES, comment_type) | |||
|
1568 | ) | |||
|
1569 | ||||
|
1570 | comments = CommentsModel().get_repository_comments( | |||
|
1571 | repo=repo, comment_type=comment_type, user=user, commit_id=commit_id) | |||
|
1572 | return comments | |||
|
1573 | ||||
|
1574 | ||||
|
1575 | @jsonrpc_method() | |||
1509 | def grant_user_permission(request, apiuser, repoid, userid, perm): |
|
1576 | def grant_user_permission(request, apiuser, repoid, userid, perm): | |
1510 | """ |
|
1577 | """ | |
1511 | Grant permissions for the specified user on the given repository, |
|
1578 | Grant permissions for the specified user on the given repository, |
@@ -125,6 +125,24 b' class CommentsModel(BaseModel):' | |||||
125 |
|
125 | |||
126 | return comment_versions |
|
126 | return comment_versions | |
127 |
|
127 | |||
|
128 | def get_repository_comments(self, repo, comment_type=None, user=None, commit_id=None): | |||
|
129 | qry = Session().query(ChangesetComment) \ | |||
|
130 | .filter(ChangesetComment.repo == repo) | |||
|
131 | ||||
|
132 | if comment_type and comment_type in ChangesetComment.COMMENT_TYPES: | |||
|
133 | qry = qry.filter(ChangesetComment.comment_type == comment_type) | |||
|
134 | ||||
|
135 | if user: | |||
|
136 | user = self._get_user(user) | |||
|
137 | if user: | |||
|
138 | qry = qry.filter(ChangesetComment.user_id == user.user_id) | |||
|
139 | ||||
|
140 | if commit_id: | |||
|
141 | qry = qry.filter(ChangesetComment.revision == commit_id) | |||
|
142 | ||||
|
143 | qry = qry.order_by(ChangesetComment.created_on) | |||
|
144 | return qry.all() | |||
|
145 | ||||
128 | def get_repository_unresolved_todos(self, repo): |
|
146 | def get_repository_unresolved_todos(self, repo): | |
129 | todos = Session().query(ChangesetComment) \ |
|
147 | todos = Session().query(ChangesetComment) \ | |
130 | .filter(ChangesetComment.repo == repo) \ |
|
148 | .filter(ChangesetComment.repo == repo) \ |
@@ -3497,7 +3497,8 b' class ChangesetComment(Base, BaseModel):' | |||||
3497 | 'comment_f_path': comment.f_path, |
|
3497 | 'comment_f_path': comment.f_path, | |
3498 | 'comment_lineno': comment.line_no, |
|
3498 | 'comment_lineno': comment.line_no, | |
3499 | 'comment_author': comment.author, |
|
3499 | 'comment_author': comment.author, | |
3500 | 'comment_created_on': comment.created_on |
|
3500 | 'comment_created_on': comment.created_on, | |
|
3501 | 'comment_resolved_by': self.resolved | |||
3501 | } |
|
3502 | } | |
3502 | return data |
|
3503 | return data | |
3503 |
|
3504 |
General Comments 0
You need to be logged in to leave comments.
Login now