api: added get_comment method, and return versions for comments to allow simple edits via API.
This commit is contained in:
parent
97a12ce1b9
commit
9cadf302eb
7 changed files with 204 additions and 91 deletions
|
|
@ -87,8 +87,9 @@ class TestApi(object):
|
|||
id_, params = build_data(self.apikey, 'comment', args='xx')
|
||||
response = api_call(self.app, params)
|
||||
expected = 'No such method: comment. ' \
|
||||
'Similar methods: changeset_comment, comment_pull_request, edit_comment, ' \
|
||||
'get_pull_request_comments, comment_commit, get_repo_comments'
|
||||
'Similar methods: changeset_comment, comment_pull_request, ' \
|
||||
'get_pull_request_comments, comment_commit, edit_comment, ' \
|
||||
'get_comment, get_repo_comments'
|
||||
assert_error(id_, expected, given=response.body)
|
||||
|
||||
def test_api_disabled_user(self, request):
|
||||
|
|
|
|||
|
|
@ -37,8 +37,10 @@ class TestGetMethod(object):
|
|||
id_, params = build_data(self.apikey, 'get_method', pattern='*comment*')
|
||||
response = api_call(self.app, params)
|
||||
|
||||
expected = ['changeset_comment', 'comment_pull_request', 'edit_comment',
|
||||
'get_pull_request_comments', 'comment_commit', 'get_repo_comments']
|
||||
expected = [
|
||||
'changeset_comment', 'comment_pull_request', 'get_pull_request_comments',
|
||||
'comment_commit', 'edit_comment', 'get_comment', 'get_repo_comments'
|
||||
]
|
||||
assert_ok(id_, expected, given=response.body)
|
||||
|
||||
def test_get_methods_on_single_match(self):
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ class TestGetPullRequestComments(object):
|
|||
'comment_type': 'note',
|
||||
'comment_resolved_by': None,
|
||||
'pull_request_version': None,
|
||||
'comment_last_version': 0,
|
||||
'comment_commit_id': None,
|
||||
'comment_pull_request_id': pull_request.pull_request_id
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,26 +42,27 @@ def make_repo_comments_factory(request):
|
|||
comments = []
|
||||
|
||||
# general
|
||||
CommentsModel().create(
|
||||
comment = CommentsModel().create(
|
||||
text='General Comment', repo=repo, user=user, commit_id=commit_id,
|
||||
comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False)
|
||||
comments.append(comment)
|
||||
|
||||
# inline
|
||||
CommentsModel().create(
|
||||
comment = CommentsModel().create(
|
||||
text='Inline Comment', repo=repo, user=user, commit_id=commit_id,
|
||||
f_path=file_0, line_no='n1',
|
||||
comment_type=ChangesetComment.COMMENT_TYPE_NOTE, send_email=False)
|
||||
comments.append(comment)
|
||||
|
||||
# todo
|
||||
CommentsModel().create(
|
||||
comment = CommentsModel().create(
|
||||
text='INLINE TODO Comment', repo=repo, user=user, commit_id=commit_id,
|
||||
f_path=file_0, line_no='n1',
|
||||
comment_type=ChangesetComment.COMMENT_TYPE_TODO, send_email=False)
|
||||
comments.append(comment)
|
||||
|
||||
return comments
|
||||
|
||||
@request.addfinalizer
|
||||
def cleanup():
|
||||
for comment in comments:
|
||||
Session().delete(comment)
|
||||
return Make()
|
||||
|
||||
|
||||
|
|
@ -108,3 +109,34 @@ class TestGetRepo(object):
|
|||
id_, params = build_data(self.apikey, 'get_repo_comments', **api_call_params)
|
||||
response = api_call(self.app, params)
|
||||
assert_error(id_, expected, given=response.body)
|
||||
|
||||
def test_api_get_comment(self, make_repo_comments_factory, backend_hg):
|
||||
commits = [{'message': 'A'}, {'message': 'B'}]
|
||||
repo = backend_hg.create_repo(commits=commits)
|
||||
|
||||
comments = make_repo_comments_factory.make_comments(repo)
|
||||
comment_ids = [x.comment_id for x in comments]
|
||||
Session().commit()
|
||||
|
||||
for comment_id in comment_ids:
|
||||
id_, params = build_data(self.apikey, 'get_comment',
|
||||
**{'comment_id': comment_id})
|
||||
response = api_call(self.app, params)
|
||||
result = assert_call_ok(id_, given=response.body)
|
||||
assert result['comment_id'] == comment_id
|
||||
|
||||
def test_api_get_comment_no_access(self, make_repo_comments_factory, backend_hg, user_util):
|
||||
commits = [{'message': 'A'}, {'message': 'B'}]
|
||||
repo = backend_hg.create_repo(commits=commits)
|
||||
comments = make_repo_comments_factory.make_comments(repo)
|
||||
comment_id = comments[0].comment_id
|
||||
|
||||
test_user = user_util.create_user()
|
||||
user_util.grant_user_permission_to_repo(repo, test_user, 'repository.none')
|
||||
|
||||
id_, params = build_data(test_user.api_key, 'get_comment',
|
||||
**{'comment_id': comment_id})
|
||||
response = api_call(self.app, params)
|
||||
assert_error(id_,
|
||||
expected='comment `{}` does not exist'.format(comment_id),
|
||||
given=response.body)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ from rhodecode.api.utils import (
|
|||
get_pull_request_or_error, get_commit_or_error, get_user_or_error,
|
||||
validate_repo_permissions, resolve_ref_or_error, validate_set_owner_permissions)
|
||||
from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
|
||||
from rhodecode.lib.exceptions import CommentVersionMismatch
|
||||
from rhodecode.lib.base import vcs_operation_context
|
||||
from rhodecode.lib.utils2 import str2bool
|
||||
from rhodecode.model.changeset_status import ChangesetStatusModel
|
||||
|
|
@ -36,8 +35,7 @@ from rhodecode.model.db import Session, ChangesetStatus, ChangesetComment, PullR
|
|||
from rhodecode.model.pull_request import PullRequestModel, MergeCheck
|
||||
from rhodecode.model.settings import SettingsModel
|
||||
from rhodecode.model.validation_schema import Invalid
|
||||
from rhodecode.model.validation_schema.schemas.reviewer_schema import(
|
||||
ReviewerListSchema)
|
||||
from rhodecode.model.validation_schema.schemas.reviewer_schema import ReviewerListSchema
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -380,6 +378,7 @@ def get_pull_request_comments(
|
|||
},
|
||||
"comment_text": "Example text",
|
||||
"comment_type": null,
|
||||
"comment_last_version: 0,
|
||||
"pull_request_version": null,
|
||||
"comment_commit_id": None,
|
||||
"comment_pull_request_id": <pull_request_id>
|
||||
|
|
@ -632,79 +631,6 @@ def comment_pull_request(
|
|||
return data
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
def edit_comment(
|
||||
request, apiuser, message, comment_id, version,
|
||||
userid=Optional(OAttr('apiuser')),
|
||||
):
|
||||
"""
|
||||
Edit comment on the pull request or commit,
|
||||
specified by the `comment_id` and version. Initially version should be 0
|
||||
|
||||
:param apiuser: This is filled automatically from the |authtoken|.
|
||||
:type apiuser: AuthUser
|
||||
:param comment_id: Specify the comment_id for editing
|
||||
:type comment_id: int
|
||||
:param version: version of the comment that will be created, starts from 0
|
||||
:type version: int
|
||||
:param message: The text content of the comment.
|
||||
:type message: str
|
||||
:param userid: Comment on the pull request as this user
|
||||
:type userid: Optional(str or int)
|
||||
|
||||
Example output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
id : <id_given_in_input>
|
||||
result : {
|
||||
"comment_history_id": "<Integer>",
|
||||
"version": "<Integer>",
|
||||
},
|
||||
error : null
|
||||
"""
|
||||
|
||||
auth_user = apiuser
|
||||
comment = ChangesetComment.get(comment_id)
|
||||
|
||||
is_super_admin = has_superadmin_permission(apiuser)
|
||||
is_repo_admin = HasRepoPermissionAnyApi('repository.admin') \
|
||||
(user=apiuser, repo_name=comment.repo.repo_name)
|
||||
|
||||
if not isinstance(userid, Optional):
|
||||
if is_super_admin or is_repo_admin:
|
||||
apiuser = get_user_or_error(userid)
|
||||
auth_user = apiuser.AuthUser()
|
||||
else:
|
||||
raise JSONRPCError('userid is not the same as your user')
|
||||
|
||||
comment_author = comment.author.user_id == auth_user.user_id
|
||||
if not (comment.immutable is False and (is_super_admin or is_repo_admin) or comment_author):
|
||||
raise JSONRPCError("you don't have access to edit this comment")
|
||||
|
||||
try:
|
||||
comment_history = CommentsModel().edit(
|
||||
comment_id=comment_id,
|
||||
text=message,
|
||||
auth_user=auth_user,
|
||||
version=version,
|
||||
)
|
||||
Session().commit()
|
||||
except CommentVersionMismatch:
|
||||
raise JSONRPCError(
|
||||
'comment ({}) version ({}) mismatch'.format(comment_id, version)
|
||||
)
|
||||
if not comment_history and not message:
|
||||
raise JSONRPCError(
|
||||
"comment ({}) can't be changed with empty string".format(comment_id)
|
||||
)
|
||||
data = {
|
||||
'comment_history_id': comment_history.comment_history_id if comment_history else None,
|
||||
'version': comment_history.version if comment_history else None,
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
def create_pull_request(
|
||||
request, apiuser, source_repo, target_repo, source_ref, target_ref,
|
||||
|
|
|
|||
|
|
@ -31,11 +31,15 @@ from rhodecode.api.utils import (
|
|||
validate_set_owner_permissions)
|
||||
from rhodecode.lib import audit_logger, rc_cache
|
||||
from rhodecode.lib import repo_maintenance
|
||||
from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
|
||||
from rhodecode.lib.auth import (
|
||||
HasPermissionAnyApi, HasUserGroupPermissionAnyApi,
|
||||
HasRepoPermissionAnyApi)
|
||||
from rhodecode.lib.celerylib.utils import get_task_id
|
||||
from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int, safe_unicode
|
||||
from rhodecode.lib.utils2 import (
|
||||
str2bool, time_to_datetime, safe_str, safe_int, safe_unicode)
|
||||
from rhodecode.lib.ext_json import json
|
||||
from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
|
||||
from rhodecode.lib.exceptions import (
|
||||
StatusChangeOnClosedPullRequestError, CommentVersionMismatch)
|
||||
from rhodecode.lib.vcs import RepositoryError
|
||||
from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
|
||||
from rhodecode.model.changeset_status import ChangesetStatusModel
|
||||
|
|
@ -1719,7 +1723,8 @@ def get_repo_comments(request, apiuser, repoid,
|
|||
"comment_resolved_by": null,
|
||||
"comment_status": [],
|
||||
"comment_text": "This file needs a header",
|
||||
"comment_type": "todo"
|
||||
"comment_type": "todo",
|
||||
"comment_last_version: 0
|
||||
}
|
||||
],
|
||||
"error" : null
|
||||
|
|
@ -1751,6 +1756,143 @@ def get_repo_comments(request, apiuser, repoid,
|
|||
return comments
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
def get_comment(request, apiuser, comment_id):
|
||||
"""
|
||||
Get single comment from repository or pull_request
|
||||
|
||||
:param apiuser: This is filled automatically from the |authtoken|.
|
||||
:type apiuser: AuthUser
|
||||
:param comment_id: comment id found in the URL of comment
|
||||
:type comment_id: str or int
|
||||
|
||||
Example error output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
{
|
||||
"id" : <id_given_in_input>,
|
||||
"result" : {
|
||||
"comment_author": <USER_DETAILS>,
|
||||
"comment_created_on": "2017-02-01T14:38:16.309",
|
||||
"comment_f_path": "file.txt",
|
||||
"comment_id": 282,
|
||||
"comment_lineno": "n1",
|
||||
"comment_resolved_by": null,
|
||||
"comment_status": [],
|
||||
"comment_text": "This file needs a header",
|
||||
"comment_type": "todo",
|
||||
"comment_last_version: 0
|
||||
},
|
||||
"error" : null
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
comment = ChangesetComment.get(comment_id)
|
||||
if not comment:
|
||||
raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
|
||||
|
||||
perms = ('repository.read', 'repository.write', 'repository.admin')
|
||||
has_comment_perm = HasRepoPermissionAnyApi(*perms)\
|
||||
(user=apiuser, repo_name=comment.repo.repo_name)
|
||||
|
||||
if not has_comment_perm:
|
||||
raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
|
||||
|
||||
return comment
|
||||
|
||||
|
||||
@jsonrpc_method()
|
||||
def edit_comment(request, apiuser, message, comment_id, version,
|
||||
userid=Optional(OAttr('apiuser'))):
|
||||
"""
|
||||
Edit comment on the pull request or commit,
|
||||
specified by the `comment_id` and version. Initially version should be 0
|
||||
|
||||
:param apiuser: This is filled automatically from the |authtoken|.
|
||||
:type apiuser: AuthUser
|
||||
:param comment_id: Specify the comment_id for editing
|
||||
:type comment_id: int
|
||||
:param version: version of the comment that will be created, starts from 0
|
||||
:type version: int
|
||||
:param message: The text content of the comment.
|
||||
:type message: str
|
||||
:param userid: Comment on the pull request as this user
|
||||
:type userid: Optional(str or int)
|
||||
|
||||
Example output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
id : <id_given_in_input>
|
||||
result : {
|
||||
"comment": "<comment data>",
|
||||
"version": "<Integer>",
|
||||
},
|
||||
error : null
|
||||
"""
|
||||
|
||||
auth_user = apiuser
|
||||
comment = ChangesetComment.get(comment_id)
|
||||
if not comment:
|
||||
raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
|
||||
|
||||
is_super_admin = has_superadmin_permission(apiuser)
|
||||
is_repo_admin = HasRepoPermissionAnyApi('repository.admin')\
|
||||
(user=apiuser, repo_name=comment.repo.repo_name)
|
||||
|
||||
if not isinstance(userid, Optional):
|
||||
if is_super_admin or is_repo_admin:
|
||||
apiuser = get_user_or_error(userid)
|
||||
auth_user = apiuser.AuthUser()
|
||||
else:
|
||||
raise JSONRPCError('userid is not the same as your user')
|
||||
|
||||
comment_author = comment.author.user_id == auth_user.user_id
|
||||
if not (comment.immutable is False and (is_super_admin or is_repo_admin) or comment_author):
|
||||
raise JSONRPCError("you don't have access to edit this comment")
|
||||
|
||||
try:
|
||||
comment_history = CommentsModel().edit(
|
||||
comment_id=comment_id,
|
||||
text=message,
|
||||
auth_user=auth_user,
|
||||
version=version,
|
||||
)
|
||||
Session().commit()
|
||||
except CommentVersionMismatch:
|
||||
raise JSONRPCError(
|
||||
'comment ({}) version ({}) mismatch'.format(comment_id, version)
|
||||
)
|
||||
if not comment_history and not message:
|
||||
raise JSONRPCError(
|
||||
"comment ({}) can't be changed with empty string".format(comment_id)
|
||||
)
|
||||
data = {
|
||||
'comment': comment,
|
||||
'version': comment_history.version if comment_history else None,
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
# TODO(marcink): write this with all required logic for deleting a comments in PR or commits
|
||||
# @jsonrpc_method()
|
||||
# def delete_comment(request, apiuser, comment_id):
|
||||
# auth_user = apiuser
|
||||
#
|
||||
# comment = ChangesetComment.get(comment_id)
|
||||
# if not comment:
|
||||
# raise JSONRPCError('comment `%s` does not exist' % (comment_id,))
|
||||
#
|
||||
# is_super_admin = has_superadmin_permission(apiuser)
|
||||
# is_repo_admin = HasRepoPermissionAnyApi('repository.admin')\
|
||||
# (user=apiuser, repo_name=comment.repo.repo_name)
|
||||
#
|
||||
# comment_author = comment.author.user_id == auth_user.user_id
|
||||
# if not (comment.immutable is False and (is_super_admin or is_repo_admin) or comment_author):
|
||||
# raise JSONRPCError("you don't have access to edit this comment")
|
||||
|
||||
@jsonrpc_method()
|
||||
def grant_user_permission(request, apiuser, repoid, userid, perm):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -3845,6 +3845,13 @@ class ChangesetComment(Base, BaseModel):
|
|||
def is_inline(self):
|
||||
return self.line_no and self.f_path
|
||||
|
||||
@property
|
||||
def last_version(self):
|
||||
version = 0
|
||||
if self.history:
|
||||
version = self.history[-1].version
|
||||
return version
|
||||
|
||||
def get_index_version(self, versions):
|
||||
return self.get_index_from_version(
|
||||
self.pull_request_version_id, versions)
|
||||
|
|
@ -3857,6 +3864,7 @@ class ChangesetComment(Base, BaseModel):
|
|||
|
||||
def get_api_data(self):
|
||||
comment = self
|
||||
|
||||
data = {
|
||||
'comment_id': comment.comment_id,
|
||||
'comment_type': comment.comment_type,
|
||||
|
|
@ -3869,6 +3877,7 @@ class ChangesetComment(Base, BaseModel):
|
|||
'comment_resolved_by': self.resolved,
|
||||
'comment_commit_id': comment.revision,
|
||||
'comment_pull_request_id': comment.pull_request_id,
|
||||
'comment_last_version': self.last_version
|
||||
}
|
||||
return data
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue