##// END OF EJS Templates
api: added get_comment method, and return versions for comments to allow simple edits via API.
marcink -
r4440:c1776819 default
parent child Browse files
Show More
@@ -87,8 +87,9 b' class TestApi(object):'
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. ' \
89 expected = 'No such method: comment. ' \
90 'Similar methods: changeset_comment, comment_pull_request, edit_comment, ' \
90 'Similar methods: changeset_comment, comment_pull_request, ' \
91 'get_pull_request_comments, comment_commit, get_repo_comments'
91 'get_pull_request_comments, comment_commit, edit_comment, ' \
92 'get_comment, get_repo_comments'
92 assert_error(id_, expected, given=response.body)
93 assert_error(id_, expected, given=response.body)
93
94
94 def test_api_disabled_user(self, request):
95 def test_api_disabled_user(self, request):
@@ -37,8 +37,10 b' class TestGetMethod(object):'
37 id_, params = build_data(self.apikey, 'get_method', pattern='*comment*')
37 id_, params = build_data(self.apikey, 'get_method', pattern='*comment*')
38 response = api_call(self.app, params)
38 response = api_call(self.app, params)
39
39
40 expected = ['changeset_comment', 'comment_pull_request', 'edit_comment',
40 expected = [
41 'get_pull_request_comments', 'comment_commit', 'get_repo_comments']
41 'changeset_comment', 'comment_pull_request', 'get_pull_request_comments',
42 'comment_commit', 'edit_comment', 'get_comment', 'get_repo_comments'
43 ]
42 assert_ok(id_, expected, given=response.body)
44 assert_ok(id_, expected, given=response.body)
43
45
44 def test_get_methods_on_single_match(self):
46 def test_get_methods_on_single_match(self):
@@ -61,6 +61,7 b' class TestGetPullRequestComments(object)'
61 'comment_type': 'note',
61 'comment_type': 'note',
62 'comment_resolved_by': None,
62 'comment_resolved_by': None,
63 'pull_request_version': None,
63 'pull_request_version': None,
64 'comment_last_version': 0,
64 'comment_commit_id': None,
65 'comment_commit_id': None,
65 'comment_pull_request_id': pull_request.pull_request_id
66 'comment_pull_request_id': pull_request.pull_request_id
66 }
67 }
@@ -42,26 +42,27 b' def make_repo_comments_factory(request):'
42 comments = []
42 comments = []
43
43
44 # general
44 # general
45 CommentsModel().create(
45 comment = CommentsModel().create(
46 text='General Comment', repo=repo, user=user, commit_id=commit_id,
46 text='General Comment', repo=repo, user=user, commit_id=commit_id,
47 comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False)
47 comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False)
48 comments.append(comment)
48
49
49 # inline
50 # inline
50 CommentsModel().create(
51 comment = CommentsModel().create(
51 text='Inline Comment', repo=repo, user=user, commit_id=commit_id,
52 text='Inline Comment', repo=repo, user=user, commit_id=commit_id,
52 f_path=file_0, line_no='n1',
53 f_path=file_0, line_no='n1',
53 comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False)
54 comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False)
55 comments.append(comment)
54
56
55 # todo
57 # todo
56 CommentsModel().create(
58 comment = CommentsModel().create(
57 text='INLINE TODO Comment', repo=repo, user=user, commit_id=commit_id,
59 text='INLINE TODO Comment', repo=repo, user=user, commit_id=commit_id,
58 f_path=file_0, line_no='n1',
60 f_path=file_0, line_no='n1',
59 comment_type=ChangesetComment.COMMENT_TYPE_TODO, send_email=False)
61 comment_type=ChangesetComment.COMMENT_TYPE_TODO, send_email=False)
62 comments.append(comment)
60
63
61 @request.addfinalizer
64 return comments
62 def cleanup():
65
63 for comment in comments:
64 Session().delete(comment)
65 return Make()
66 return Make()
66
67
67
68
@@ -108,3 +109,34 b' class TestGetRepo(object):'
108 id_, params = build_data(self.apikey, 'get_repo_comments', **api_call_params)
109 id_, params = build_data(self.apikey, 'get_repo_comments', **api_call_params)
109 response = api_call(self.app, params)
110 response = api_call(self.app, params)
110 assert_error(id_, expected, given=response.body)
111 assert_error(id_, expected, given=response.body)
112
113 def test_api_get_comment(self, make_repo_comments_factory, backend_hg):
114 commits = [{'message': 'A'}, {'message': 'B'}]
115 repo = backend_hg.create_repo(commits=commits)
116
117 comments = make_repo_comments_factory.make_comments(repo)
118 comment_ids = [x.comment_id for x in comments]
119 Session().commit()
120
121 for comment_id in comment_ids:
122 id_, params = build_data(self.apikey, 'get_comment',
123 **{'comment_id': comment_id})
124 response = api_call(self.app, params)
125 result = assert_call_ok(id_, given=response.body)
126 assert result['comment_id'] == comment_id
127
128 def test_api_get_comment_no_access(self, make_repo_comments_factory, backend_hg, user_util):
129 commits = [{'message': 'A'}, {'message': 'B'}]
130 repo = backend_hg.create_repo(commits=commits)
131 comments = make_repo_comments_factory.make_comments(repo)
132 comment_id = comments[0].comment_id
133
134 test_user = user_util.create_user()
135 user_util.grant_user_permission_to_repo(repo, test_user, 'repository.none')
136
137 id_, params = build_data(test_user.api_key, 'get_comment',
138 **{'comment_id': comment_id})
139 response = api_call(self.app, params)
140 assert_error(id_,
141 expected='comment `{}` does not exist'.format(comment_id),
142 given=response.body)
@@ -27,7 +27,6 b' from rhodecode.api.utils import ('
27 get_pull_request_or_error, get_commit_or_error, get_user_or_error,
27 get_pull_request_or_error, get_commit_or_error, get_user_or_error,
28 validate_repo_permissions, resolve_ref_or_error, validate_set_owner_permissions)
28 validate_repo_permissions, resolve_ref_or_error, validate_set_owner_permissions)
29 from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
29 from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
30 from rhodecode.lib.exceptions import CommentVersionMismatch
31 from rhodecode.lib.base import vcs_operation_context
30 from rhodecode.lib.base import vcs_operation_context
32 from rhodecode.lib.utils2 import str2bool
31 from rhodecode.lib.utils2 import str2bool
33 from rhodecode.model.changeset_status import ChangesetStatusModel
32 from rhodecode.model.changeset_status import ChangesetStatusModel
@@ -36,8 +35,7 b' from rhodecode.model.db import Session, '
36 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
35 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
37 from rhodecode.model.settings import SettingsModel
36 from rhodecode.model.settings import SettingsModel
38 from rhodecode.model.validation_schema import Invalid
37 from rhodecode.model.validation_schema import Invalid
39 from rhodecode.model.validation_schema.schemas.reviewer_schema import(
38 from rhodecode.model.validation_schema.schemas.reviewer_schema import ReviewerListSchema
40 ReviewerListSchema)
41
39
42 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
43
41
@@ -380,6 +378,7 b' def get_pull_request_comments('
380 },
378 },
381 "comment_text": "Example text",
379 "comment_text": "Example text",
382 "comment_type": null,
380 "comment_type": null,
381 "comment_last_version: 0,
383 "pull_request_version": null,
382 "pull_request_version": null,
384 "comment_commit_id": None,
383 "comment_commit_id": None,
385 "comment_pull_request_id": <pull_request_id>
384 "comment_pull_request_id": <pull_request_id>
@@ -633,79 +632,6 b' def comment_pull_request('
633
632
634
633
635 @jsonrpc_method()
634 @jsonrpc_method()
636 def edit_comment(
637 request, apiuser, message, comment_id, version,
638 userid=Optional(OAttr('apiuser')),
639 ):
640 """
641 Edit comment on the pull request or commit,
642 specified by the `comment_id` and version. Initially version should be 0
643
644 :param apiuser: This is filled automatically from the |authtoken|.
645 :type apiuser: AuthUser
646 :param comment_id: Specify the comment_id for editing
647 :type comment_id: int
648 :param version: version of the comment that will be created, starts from 0
649 :type version: int
650 :param message: The text content of the comment.
651 :type message: str
652 :param userid: Comment on the pull request as this user
653 :type userid: Optional(str or int)
654
655 Example output:
656
657 .. code-block:: bash
658
659 id : <id_given_in_input>
660 result : {
661 "comment_history_id": "<Integer>",
662 "version": "<Integer>",
663 },
664 error : null
665 """
666
667 auth_user = apiuser
668 comment = ChangesetComment.get(comment_id)
669
670 is_super_admin = has_superadmin_permission(apiuser)
671 is_repo_admin = HasRepoPermissionAnyApi('repository.admin') \
672 (user=apiuser, repo_name=comment.repo.repo_name)
673
674 if not isinstance(userid, Optional):
675 if is_super_admin or is_repo_admin:
676 apiuser = get_user_or_error(userid)
677 auth_user = apiuser.AuthUser()
678 else:
679 raise JSONRPCError('userid is not the same as your user')
680
681 comment_author = comment.author.user_id == auth_user.user_id
682 if not (comment.immutable is False and (is_super_admin or is_repo_admin) or comment_author):
683 raise JSONRPCError("you don't have access to edit this comment")
684
685 try:
686 comment_history = CommentsModel().edit(
687 comment_id=comment_id,
688 text=message,
689 auth_user=auth_user,
690 version=version,
691 )
692 Session().commit()
693 except CommentVersionMismatch:
694 raise JSONRPCError(
695 'comment ({}) version ({}) mismatch'.format(comment_id, version)
696 )
697 if not comment_history and not message:
698 raise JSONRPCError(
699 "comment ({}) can't be changed with empty string".format(comment_id)
700 )
701 data = {
702 'comment_history_id': comment_history.comment_history_id if comment_history else None,
703 'version': comment_history.version if comment_history else None,
704 }
705 return data
706
707
708 @jsonrpc_method()
709 def create_pull_request(
635 def create_pull_request(
710 request, apiuser, source_repo, target_repo, source_ref, target_ref,
636 request, apiuser, source_repo, target_repo, source_ref, target_ref,
711 owner=Optional(OAttr('apiuser')), title=Optional(''), description=Optional(''),
637 owner=Optional(OAttr('apiuser')), title=Optional(''), description=Optional(''),
@@ -31,11 +31,15 b' from rhodecode.api.utils import ('
31 validate_set_owner_permissions)
31 validate_set_owner_permissions)
32 from rhodecode.lib import audit_logger, rc_cache
32 from rhodecode.lib import audit_logger, rc_cache
33 from rhodecode.lib import repo_maintenance
33 from rhodecode.lib import repo_maintenance
34 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
34 from rhodecode.lib.auth import (
35 HasPermissionAnyApi, HasUserGroupPermissionAnyApi,
36 HasRepoPermissionAnyApi)
35 from rhodecode.lib.celerylib.utils import get_task_id
37 from rhodecode.lib.celerylib.utils import get_task_id
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int, safe_unicode
38 from rhodecode.lib.utils2 import (
39 str2bool, time_to_datetime, safe_str, safe_int, safe_unicode)
37 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
41 from rhodecode.lib.exceptions import (
42 StatusChangeOnClosedPullRequestError, CommentVersionMismatch)
39 from rhodecode.lib.vcs import RepositoryError
43 from rhodecode.lib.vcs import RepositoryError
40 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
44 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
41 from rhodecode.model.changeset_status import ChangesetStatusModel
45 from rhodecode.model.changeset_status import ChangesetStatusModel
@@ -1719,7 +1723,8 b' def get_repo_comments(request, apiuser, '
1719 "comment_resolved_by": null,
1723 "comment_resolved_by": null,
1720 "comment_status": [],
1724 "comment_status": [],
1721 "comment_text": "This file needs a header",
1725 "comment_text": "This file needs a header",
1722 "comment_type": "todo"
1726 "comment_type": "todo",
1727 "comment_last_version: 0
1723 }
1728 }
1724 ],
1729 ],
1725 "error" : null
1730 "error" : null
@@ -1752,6 +1757,143 b' def get_repo_comments(request, apiuser, '
1752
1757
1753
1758
1754 @jsonrpc_method()
1759 @jsonrpc_method()
1760 def get_comment(request, apiuser, comment_id):
1761 """
1762 Get single comment from repository or pull_request
1763
1764 :param apiuser: This is filled automatically from the |authtoken|.
1765 :type apiuser: AuthUser
1766 :param comment_id: comment id found in the URL of comment
1767 :type comment_id: str or int
1768
1769 Example error output:
1770
1771 .. code-block:: bash
1772
1773 {
1774 "id" : <id_given_in_input>,
1775 "result" : {
1776 "comment_author": <USER_DETAILS>,
1777 "comment_created_on": "2017-02-01T14:38:16.309",
1778 "comment_f_path": "file.txt",
1779 "comment_id": 282,
1780 "comment_lineno": "n1",
1781 "comment_resolved_by": null,
1782 "comment_status": [],
1783 "comment_text": "This file needs a header",
1784 "comment_type": "todo",
1785 "comment_last_version: 0
1786 },
1787 "error" : null
1788 }
1789
1790 """
1791
1792 comment = ChangesetComment.get(comment_id)
1793 if not comment:
1794 raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
1795
1796 perms = ('repository.read', 'repository.write', 'repository.admin')
1797 has_comment_perm = HasRepoPermissionAnyApi(*perms)\
1798 (user=apiuser, repo_name=comment.repo.repo_name)
1799
1800 if not has_comment_perm:
1801 raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
1802
1803 return comment
1804
1805
1806 @jsonrpc_method()
1807 def edit_comment(request, apiuser, message, comment_id, version,
1808 userid=Optional(OAttr('apiuser'))):
1809 """
1810 Edit comment on the pull request or commit,
1811 specified by the `comment_id` and version. Initially version should be 0
1812
1813 :param apiuser: This is filled automatically from the |authtoken|.
1814 :type apiuser: AuthUser
1815 :param comment_id: Specify the comment_id for editing
1816 :type comment_id: int
1817 :param version: version of the comment that will be created, starts from 0
1818 :type version: int
1819 :param message: The text content of the comment.
1820 :type message: str
1821 :param userid: Comment on the pull request as this user
1822 :type userid: Optional(str or int)
1823
1824 Example output:
1825
1826 .. code-block:: bash
1827
1828 id : <id_given_in_input>
1829 result : {
1830 "comment": "<comment data>",
1831 "version": "<Integer>",
1832 },
1833 error : null
1834 """
1835
1836 auth_user = apiuser
1837 comment = ChangesetComment.get(comment_id)
1838 if not comment:
1839 raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
1840
1841 is_super_admin = has_superadmin_permission(apiuser)
1842 is_repo_admin = HasRepoPermissionAnyApi('repository.admin')\
1843 (user=apiuser, repo_name=comment.repo.repo_name)
1844
1845 if not isinstance(userid, Optional):
1846 if is_super_admin or is_repo_admin:
1847 apiuser = get_user_or_error(userid)
1848 auth_user = apiuser.AuthUser()
1849 else:
1850 raise JSONRPCError('userid is not the same as your user')
1851
1852 comment_author = comment.author.user_id == auth_user.user_id
1853 if not (comment.immutable is False and (is_super_admin or is_repo_admin) or comment_author):
1854 raise JSONRPCError("you don't have access to edit this comment")
1855
1856 try:
1857 comment_history = CommentsModel().edit(
1858 comment_id=comment_id,
1859 text=message,
1860 auth_user=auth_user,
1861 version=version,
1862 )
1863 Session().commit()
1864 except CommentVersionMismatch:
1865 raise JSONRPCError(
1866 'comment ({}) version ({}) mismatch'.format(comment_id, version)
1867 )
1868 if not comment_history and not message:
1869 raise JSONRPCError(
1870 "comment ({}) can't be changed with empty string".format(comment_id)
1871 )
1872 data = {
1873 'comment': comment,
1874 'version': comment_history.version if comment_history else None,
1875 }
1876 return data
1877
1878
1879 # TODO(marcink): write this with all required logic for deleting a comments in PR or commits
1880 # @jsonrpc_method()
1881 # def delete_comment(request, apiuser, comment_id):
1882 # auth_user = apiuser
1883 #
1884 # comment = ChangesetComment.get(comment_id)
1885 # if not comment:
1886 # raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
1887 #
1888 # is_super_admin = has_superadmin_permission(apiuser)
1889 # is_repo_admin = HasRepoPermissionAnyApi('repository.admin')\
1890 # (user=apiuser, repo_name=comment.repo.repo_name)
1891 #
1892 # comment_author = comment.author.user_id == auth_user.user_id
1893 # if not (comment.immutable is False and (is_super_admin or is_repo_admin) or comment_author):
1894 # raise JSONRPCError("you don't have access to edit this comment")
1895
1896 @jsonrpc_method()
1755 def grant_user_permission(request, apiuser, repoid, userid, perm):
1897 def grant_user_permission(request, apiuser, repoid, userid, perm):
1756 """
1898 """
1757 Grant permissions for the specified user on the given repository,
1899 Grant permissions for the specified user on the given repository,
@@ -3845,6 +3845,13 b' class ChangesetComment(Base, BaseModel):'
3845 def is_inline(self):
3845 def is_inline(self):
3846 return self.line_no and self.f_path
3846 return self.line_no and self.f_path
3847
3847
3848 @property
3849 def last_version(self):
3850 version = 0
3851 if self.history:
3852 version = self.history[-1].version
3853 return version
3854
3848 def get_index_version(self, versions):
3855 def get_index_version(self, versions):
3849 return self.get_index_from_version(
3856 return self.get_index_from_version(
3850 self.pull_request_version_id, versions)
3857 self.pull_request_version_id, versions)
@@ -3857,6 +3864,7 b' class ChangesetComment(Base, BaseModel):'
3857
3864
3858 def get_api_data(self):
3865 def get_api_data(self):
3859 comment = self
3866 comment = self
3867
3860 data = {
3868 data = {
3861 'comment_id': comment.comment_id,
3869 'comment_id': comment.comment_id,
3862 'comment_type': comment.comment_type,
3870 'comment_type': comment.comment_type,
@@ -3869,6 +3877,7 b' class ChangesetComment(Base, BaseModel):'
3869 'comment_resolved_by': self.resolved,
3877 'comment_resolved_by': self.resolved,
3870 'comment_commit_id': comment.revision,
3878 'comment_commit_id': comment.revision,
3871 'comment_pull_request_id': comment.pull_request_id,
3879 'comment_pull_request_id': comment.pull_request_id,
3880 'comment_last_version': self.last_version
3872 }
3881 }
3873 return data
3882 return data
3874
3883
General Comments 0
You need to be logged in to leave comments. Login now