##// END OF EJS Templates
api: added function to fetch comments for a repository.
marcink -
r3435:2058136f default
parent child Browse files
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