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 | 86 | def test_api_non_existing_method_have_similar(self, request): |
|
87 | 87 | id_, params = build_data(self.apikey, 'comment', args='xx') |
|
88 | 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 | 92 | assert_error(id_, expected, given=response.body) |
|
91 | 93 | |
|
92 | 94 | def test_api_disabled_user(self, request): |
@@ -38,7 +38,7 b' class TestGetMethod(object):' | |||
|
38 | 38 | response = api_call(self.app, params) |
|
39 | 39 | |
|
40 | 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 | 42 | assert_ok(id_, expected, given=response.body) |
|
43 | 43 | |
|
44 | 44 | def test_get_methods_on_single_match(self): |
@@ -59,6 +59,7 b' class TestGetPullRequestComments(object)' | |||
|
59 | 59 | 'status_lbl': 'Under Review'}, |
|
60 | 60 | 'comment_text': 'Auto status change to |new_status|\n\n.. |new_status| replace:: *"Under Review"*', |
|
61 | 61 | 'comment_type': 'note', |
|
62 | 'comment_resolved_by': None, | |
|
62 | 63 | 'pull_request_version': None} |
|
63 | 64 | ] |
|
64 | 65 | assert_ok(id_, expected, response.body) |
@@ -28,6 +28,19 b' from rhodecode.lib.ext_json import json' | |||
|
28 | 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 | 44 | def assert_ok(id_, expected, given): |
|
32 | 45 | expected = jsonify({ |
|
33 | 46 | 'id': id_, |
@@ -55,8 +68,6 b' def jsonify(obj):' | |||
|
55 | 68 | def build_data(apikey, method, **kw): |
|
56 | 69 | """ |
|
57 | 70 | Builds API data with given random ID |
|
58 | ||
|
59 | :param random_id: | |
|
60 | 71 | """ |
|
61 | 72 | random_id = random.randrange(1, 9999) |
|
62 | 73 | return random_id, json.dumps({ |
@@ -1506,6 +1506,73 b' def comment_commit(' | |||
|
1506 | 1506 | |
|
1507 | 1507 | |
|
1508 | 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 | 1576 | def grant_user_permission(request, apiuser, repoid, userid, perm): |
|
1510 | 1577 | """ |
|
1511 | 1578 | Grant permissions for the specified user on the given repository, |
@@ -125,6 +125,24 b' class CommentsModel(BaseModel):' | |||
|
125 | 125 | |
|
126 | 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 | 146 | def get_repository_unresolved_todos(self, repo): |
|
129 | 147 | todos = Session().query(ChangesetComment) \ |
|
130 | 148 | .filter(ChangesetComment.repo == repo) \ |
@@ -3497,7 +3497,8 b' class ChangesetComment(Base, BaseModel):' | |||
|
3497 | 3497 | 'comment_f_path': comment.f_path, |
|
3498 | 3498 | 'comment_lineno': comment.line_no, |
|
3499 | 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 | 3503 | return data |
|
3503 | 3504 |
General Comments 0
You need to be logged in to leave comments.
Login now