Show More
@@ -43,6 +43,7 b' class TestClosePullRequest(object):' | |||
|
43 | 43 | response = api_call(self.app, params) |
|
44 | 44 | expected = { |
|
45 | 45 | 'pull_request_id': pull_request_id, |
|
46 | 'close_status': 'Rejected', | |
|
46 | 47 | 'closed': True, |
|
47 | 48 | } |
|
48 | 49 | assert_ok(id_, expected, response.body) |
@@ -21,6 +21,7 b'' | |||
|
21 | 21 | |
|
22 | 22 | import logging |
|
23 | 23 | |
|
24 | from rhodecode import events | |
|
24 | 25 | from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCValidationError |
|
25 | 26 | from rhodecode.api.utils import ( |
|
26 | 27 | has_superadmin_permission, Optional, OAttr, get_repo_or_error, |
@@ -35,8 +36,8 b' from rhodecode.model.db import Session, ' | |||
|
35 | 36 | from rhodecode.model.pull_request import PullRequestModel, MergeCheck |
|
36 | 37 | from rhodecode.model.settings import SettingsModel |
|
37 | 38 | from rhodecode.model.validation_schema import Invalid |
|
38 |
from rhodecode.model.validation_schema.schemas.reviewer_schema import |
|
|
39 | ReviewerListSchema | |
|
39 | from rhodecode.model.validation_schema.schemas.reviewer_schema import( | |
|
40 | ReviewerListSchema) | |
|
40 | 41 | |
|
41 | 42 | log = logging.getLogger(__name__) |
|
42 | 43 | |
@@ -227,8 +228,9 b' def get_pull_requests(request, apiuser, ' | |||
|
227 | 228 | |
|
228 | 229 | |
|
229 | 230 | @jsonrpc_method() |
|
230 | def merge_pull_request(request, apiuser, repoid, pullrequestid, | |
|
231 | userid=Optional(OAttr('apiuser'))): | |
|
231 | def merge_pull_request( | |
|
232 | request, apiuser, repoid, pullrequestid, | |
|
233 | userid=Optional(OAttr('apiuser'))): | |
|
232 | 234 | """ |
|
233 | 235 | Merge the pull request specified by `pullrequestid` into its target |
|
234 | 236 | repository. |
@@ -308,63 +310,6 b' def merge_pull_request(request, apiuser,' | |||
|
308 | 310 | |
|
309 | 311 | |
|
310 | 312 | @jsonrpc_method() |
|
311 | def close_pull_request(request, apiuser, repoid, pullrequestid, | |
|
312 | userid=Optional(OAttr('apiuser'))): | |
|
313 | """ | |
|
314 | Close the pull request specified by `pullrequestid`. | |
|
315 | ||
|
316 | :param apiuser: This is filled automatically from the |authtoken|. | |
|
317 | :type apiuser: AuthUser | |
|
318 | :param repoid: Repository name or repository ID to which the pull | |
|
319 | request belongs. | |
|
320 | :type repoid: str or int | |
|
321 | :param pullrequestid: ID of the pull request to be closed. | |
|
322 | :type pullrequestid: int | |
|
323 | :param userid: Close the pull request as this user. | |
|
324 | :type userid: Optional(str or int) | |
|
325 | ||
|
326 | Example output: | |
|
327 | ||
|
328 | .. code-block:: bash | |
|
329 | ||
|
330 | "id": <id_given_in_input>, | |
|
331 | "result": { | |
|
332 | "pull_request_id": "<int>", | |
|
333 | "closed": "<bool>" | |
|
334 | }, | |
|
335 | "error": null | |
|
336 | ||
|
337 | """ | |
|
338 | repo = get_repo_or_error(repoid) | |
|
339 | if not isinstance(userid, Optional): | |
|
340 | if (has_superadmin_permission(apiuser) or | |
|
341 | HasRepoPermissionAnyApi('repository.admin')( | |
|
342 | user=apiuser, repo_name=repo.repo_name)): | |
|
343 | apiuser = get_user_or_error(userid) | |
|
344 | else: | |
|
345 | raise JSONRPCError('userid is not the same as your user') | |
|
346 | ||
|
347 | pull_request = get_pull_request_or_error(pullrequestid) | |
|
348 | if not PullRequestModel().check_user_update( | |
|
349 | pull_request, apiuser, api=True): | |
|
350 | raise JSONRPCError( | |
|
351 | 'pull request `%s` close failed, no permission to close.' % ( | |
|
352 | pullrequestid,)) | |
|
353 | if pull_request.is_closed(): | |
|
354 | raise JSONRPCError( | |
|
355 | 'pull request `%s` is already closed' % (pullrequestid,)) | |
|
356 | ||
|
357 | PullRequestModel().close_pull_request( | |
|
358 | pull_request.pull_request_id, apiuser) | |
|
359 | Session().commit() | |
|
360 | data = { | |
|
361 | 'pull_request_id': pull_request.pull_request_id, | |
|
362 | 'closed': True, | |
|
363 | } | |
|
364 | return data | |
|
365 | ||
|
366 | ||
|
367 | @jsonrpc_method() | |
|
368 | 313 | def comment_pull_request( |
|
369 | 314 | request, apiuser, repoid, pullrequestid, message=Optional(None), |
|
370 | 315 | commit_id=Optional(None), status=Optional(None), |
@@ -610,7 +555,7 b' def create_pull_request(' | |||
|
610 | 555 | def update_pull_request( |
|
611 | 556 | request, apiuser, repoid, pullrequestid, title=Optional(''), |
|
612 | 557 | description=Optional(''), reviewers=Optional(None), |
|
613 |
update_commits=Optional(None) |
|
|
558 | update_commits=Optional(None)): | |
|
614 | 559 | """ |
|
615 | 560 | Updates a pull request. |
|
616 | 561 | |
@@ -632,8 +577,6 b' def update_pull_request(' | |||
|
632 | 577 | |
|
633 | 578 | :param update_commits: Trigger update of commits for this pull request |
|
634 | 579 | :type: update_commits: Optional(bool) |
|
635 | :param close_pull_request: Close this pull request with rejected state | |
|
636 | :type: close_pull_request: Optional(bool) | |
|
637 | 580 | |
|
638 | 581 | Example output: |
|
639 | 582 | |
@@ -675,7 +618,6 b' def update_pull_request(' | |||
|
675 | 618 | 'pull request `%s` update failed, pull request is closed' % ( |
|
676 | 619 | pullrequestid,)) |
|
677 | 620 | |
|
678 | ||
|
679 | 621 | reviewer_objects = Optional.extract(reviewers) or [] |
|
680 | 622 | if reviewer_objects: |
|
681 | 623 | schema = ReviewerListSchema() |
@@ -718,11 +660,6 b' def update_pull_request(' | |||
|
718 | 660 | [get_user_or_error(n).username for n in removed_reviewers]) |
|
719 | 661 | Session().commit() |
|
720 | 662 | |
|
721 | if str2bool(Optional.extract(close_pull_request)): | |
|
722 | PullRequestModel().close_pull_request_with_comment( | |
|
723 | pull_request, apiuser, repo) | |
|
724 | Session().commit() | |
|
725 | ||
|
726 | 663 | data = { |
|
727 | 664 | 'msg': 'Updated pull request `{}`'.format( |
|
728 | 665 | pull_request.pull_request_id), |
@@ -732,3 +669,80 b' def update_pull_request(' | |||
|
732 | 669 | } |
|
733 | 670 | |
|
734 | 671 | return data |
|
672 | ||
|
673 | ||
|
674 | @jsonrpc_method() | |
|
675 | def close_pull_request( | |
|
676 | request, apiuser, repoid, pullrequestid, | |
|
677 | userid=Optional(OAttr('apiuser')), message=Optional('')): | |
|
678 | """ | |
|
679 | Close the pull request specified by `pullrequestid`. | |
|
680 | ||
|
681 | :param apiuser: This is filled automatically from the |authtoken|. | |
|
682 | :type apiuser: AuthUser | |
|
683 | :param repoid: Repository name or repository ID to which the pull | |
|
684 | request belongs. | |
|
685 | :type repoid: str or int | |
|
686 | :param pullrequestid: ID of the pull request to be closed. | |
|
687 | :type pullrequestid: int | |
|
688 | :param userid: Close the pull request as this user. | |
|
689 | :type userid: Optional(str or int) | |
|
690 | :param message: Optional message to close the Pull Request with. If not | |
|
691 | specified it will be generated automatically. | |
|
692 | :type message: Optional(str) | |
|
693 | ||
|
694 | Example output: | |
|
695 | ||
|
696 | .. code-block:: bash | |
|
697 | ||
|
698 | "id": <id_given_in_input>, | |
|
699 | "result": { | |
|
700 | "pull_request_id": "<int>", | |
|
701 | "close_status": "<str:status_lbl>, | |
|
702 | "closed": "<bool>" | |
|
703 | }, | |
|
704 | "error": null | |
|
705 | ||
|
706 | """ | |
|
707 | _ = request.translate | |
|
708 | ||
|
709 | repo = get_repo_or_error(repoid) | |
|
710 | if not isinstance(userid, Optional): | |
|
711 | if (has_superadmin_permission(apiuser) or | |
|
712 | HasRepoPermissionAnyApi('repository.admin')( | |
|
713 | user=apiuser, repo_name=repo.repo_name)): | |
|
714 | apiuser = get_user_or_error(userid) | |
|
715 | else: | |
|
716 | raise JSONRPCError('userid is not the same as your user') | |
|
717 | ||
|
718 | pull_request = get_pull_request_or_error(pullrequestid) | |
|
719 | ||
|
720 | if pull_request.is_closed(): | |
|
721 | raise JSONRPCError( | |
|
722 | 'pull request `%s` is already closed' % (pullrequestid,)) | |
|
723 | ||
|
724 | # only owner or admin or person with write permissions | |
|
725 | allowed_to_close = PullRequestModel().check_user_update( | |
|
726 | pull_request, apiuser, api=True) | |
|
727 | ||
|
728 | if not allowed_to_close: | |
|
729 | raise JSONRPCError( | |
|
730 | 'pull request `%s` close failed, no permission to close.' % ( | |
|
731 | pullrequestid,)) | |
|
732 | ||
|
733 | # message we're using to close the PR, else it's automatically generated | |
|
734 | message = Optional.extract(message) | |
|
735 | ||
|
736 | # finally close the PR, with proper message comment | |
|
737 | comment, status = PullRequestModel().close_pull_request_with_comment( | |
|
738 | pull_request, apiuser, repo, message=message) | |
|
739 | status_lbl = ChangesetStatus.get_status_lbl(status) | |
|
740 | ||
|
741 | Session().commit() | |
|
742 | ||
|
743 | data = { | |
|
744 | 'pull_request_id': pull_request.pull_request_id, | |
|
745 | 'close_status': status_lbl, | |
|
746 | 'closed': True, | |
|
747 | } | |
|
748 | return data |
@@ -368,7 +368,6 b' class ChangesetController(BaseRepoContro' | |||
|
368 | 368 | comment_type=comment_type, |
|
369 | 369 | resolves_comment_id=resolves_comment_id |
|
370 | 370 | ) |
|
371 | c.inline_comment = True if comment.line_no else False | |
|
372 | 371 | |
|
373 | 372 | # get status if set ! |
|
374 | 373 | if status: |
@@ -306,8 +306,6 b' class PullrequestsController(BaseRepoCon' | |||
|
306 | 306 | pull_request.reviewer_data) |
|
307 | 307 | elif str2bool(request.POST.get('update_commits', 'false')): |
|
308 | 308 | self._update_commits(pull_request) |
|
309 | elif str2bool(request.POST.get('close_pull_request', 'false')): | |
|
310 | self._reject_close(pull_request) | |
|
311 | 309 | elif str2bool(request.POST.get('edit_pull_request', 'false')): |
|
312 | 310 | self._edit_pull_request(pull_request) |
|
313 | 311 | else: |
@@ -462,14 +460,6 b' class PullrequestsController(BaseRepoCon' | |||
|
462 | 460 | h.flash(_('Pull request reviewers updated.'), category='success') |
|
463 | 461 | Session().commit() |
|
464 | 462 | |
|
465 | def _reject_close(self, pull_request): | |
|
466 | if pull_request.is_closed(): | |
|
467 | raise HTTPForbidden() | |
|
468 | ||
|
469 | PullRequestModel().close_pull_request_with_comment( | |
|
470 | pull_request, c.rhodecode_user, c.rhodecode_db_repo) | |
|
471 | Session().commit() | |
|
472 | ||
|
473 | 463 | @LoginRequired() |
|
474 | 464 | @NotAnonymous() |
|
475 | 465 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
@@ -888,6 +878,7 b' class PullrequestsController(BaseRepoCon' | |||
|
888 | 878 | pull_request_id = safe_int(pull_request_id) |
|
889 | 879 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
890 | 880 | if pull_request.is_closed(): |
|
881 | log.debug('comment: forbidden because pull request is closed') | |
|
891 | 882 | raise HTTPForbidden() |
|
892 | 883 | |
|
893 | 884 | status = request.POST.get('changeset_status', None) |
@@ -896,83 +887,78 b' class PullrequestsController(BaseRepoCon' | |||
|
896 | 887 | resolves_comment_id = request.POST.get('resolves_comment_id', None) |
|
897 | 888 | close_pull_request = request.POST.get('close_pull_request') |
|
898 | 889 | |
|
899 | close_pr = False | |
|
900 | # only owner or admin or person with write permissions | |
|
901 | allowed_to_close = PullRequestModel().check_user_update( | |
|
902 |
|
|
|
890 | # the logic here should work like following, if we submit close | |
|
891 | # pr comment, use `close_pull_request_with_comment` function | |
|
892 | # else handle regular comment logic | |
|
893 | user = c.rhodecode_user | |
|
894 | repo = c.rhodecode_db_repo | |
|
903 | 895 | |
|
904 |
if close_pull_request |
|
|
905 | close_pr = True | |
|
906 | pull_request_review_status = pull_request.calculated_review_status() | |
|
907 | if pull_request_review_status == ChangesetStatus.STATUS_APPROVED: | |
|
908 | # approved only if we have voting consent | |
|
909 | status = ChangesetStatus.STATUS_APPROVED | |
|
910 | else: | |
|
911 | status = ChangesetStatus.STATUS_REJECTED | |
|
896 | if close_pull_request: | |
|
897 | # only owner or admin or person with write permissions | |
|
898 | allowed_to_close = PullRequestModel().check_user_update( | |
|
899 | pull_request, c.rhodecode_user) | |
|
900 | if not allowed_to_close: | |
|
901 | log.debug('comment: forbidden because not allowed to close ' | |
|
902 | 'pull request %s', pull_request_id) | |
|
903 | raise HTTPForbidden() | |
|
904 | comment, status = PullRequestModel().close_pull_request_with_comment( | |
|
905 | pull_request, user, repo, message=text) | |
|
906 | Session().flush() | |
|
907 | events.trigger( | |
|
908 | events.PullRequestCommentEvent(pull_request, comment)) | |
|
912 | 909 | |
|
913 | allowed_to_change_status = PullRequestModel().check_user_change_status( | |
|
914 | pull_request, c.rhodecode_user) | |
|
910 | else: | |
|
911 | # regular comment case, could be inline, or one with status. | |
|
912 | # for that one we check also permissions | |
|
913 | ||
|
914 | allowed_to_change_status = PullRequestModel().check_user_change_status( | |
|
915 | pull_request, c.rhodecode_user) | |
|
916 | ||
|
917 | if status and allowed_to_change_status: | |
|
918 | message = (_('Status change %(transition_icon)s %(status)s') | |
|
919 | % {'transition_icon': '>', | |
|
920 | 'status': ChangesetStatus.get_status_lbl(status)}) | |
|
921 | text = text or message | |
|
915 | 922 | |
|
916 | if status and allowed_to_change_status: | |
|
917 | message = (_('Status change %(transition_icon)s %(status)s') | |
|
918 | % {'transition_icon': '>', | |
|
919 | 'status': ChangesetStatus.get_status_lbl(status)}) | |
|
920 | if close_pr: | |
|
921 | message = _('Closing with') + ' ' + message | |
|
922 | text = text or message | |
|
923 | comm = CommentsModel().create( | |
|
924 | text=text, | |
|
925 | repo=c.rhodecode_db_repo.repo_id, | |
|
926 | user=c.rhodecode_user.user_id, | |
|
927 | pull_request=pull_request_id, | |
|
928 | f_path=request.POST.get('f_path'), | |
|
929 | line_no=request.POST.get('line'), | |
|
930 | status_change=(ChangesetStatus.get_status_lbl(status) | |
|
931 |
|
|
|
932 | status_change_type=(status | |
|
933 | if status and allowed_to_change_status else None), | |
|
934 | closing_pr=close_pr, | |
|
935 | comment_type=comment_type, | |
|
936 | resolves_comment_id=resolves_comment_id | |
|
937 | ) | |
|
923 | comment = CommentsModel().create( | |
|
924 | text=text, | |
|
925 | repo=c.rhodecode_db_repo.repo_id, | |
|
926 | user=c.rhodecode_user.user_id, | |
|
927 | pull_request=pull_request_id, | |
|
928 | f_path=request.POST.get('f_path'), | |
|
929 | line_no=request.POST.get('line'), | |
|
930 | status_change=(ChangesetStatus.get_status_lbl(status) | |
|
931 | if status and allowed_to_change_status else None), | |
|
932 | status_change_type=(status | |
|
933 | if status and allowed_to_change_status else None), | |
|
934 | comment_type=comment_type, | |
|
935 | resolves_comment_id=resolves_comment_id | |
|
936 | ) | |
|
937 | ||
|
938 | if allowed_to_change_status: | |
|
939 | # calculate old status before we change it | |
|
940 | old_calculated_status = pull_request.calculated_review_status() | |
|
938 | 941 | |
|
939 | if allowed_to_change_status: | |
|
940 | old_calculated_status = pull_request.calculated_review_status() | |
|
941 | # get status if set ! | |
|
942 | if status: | |
|
943 | ChangesetStatusModel().set_status( | |
|
944 |
c.rhodecode_ |
|
|
945 |
|
|
|
946 | c.rhodecode_user.user_id, | |
|
947 |
|
|
|
948 | pull_request=pull_request_id | |
|
949 | ) | |
|
942 | # get status if set ! | |
|
943 | if status: | |
|
944 | ChangesetStatusModel().set_status( | |
|
945 | c.rhodecode_db_repo.repo_id, | |
|
946 | status, | |
|
947 | c.rhodecode_user.user_id, | |
|
948 | comment, | |
|
949 | pull_request=pull_request_id | |
|
950 | ) | |
|
950 | 951 | |
|
951 | Session().flush() | |
|
952 | events.trigger(events.PullRequestCommentEvent(pull_request, comm)) | |
|
953 | # we now calculate the status of pull request, and based on that | |
|
954 | # calculation we set the commits status | |
|
955 | calculated_status = pull_request.calculated_review_status() | |
|
956 | if old_calculated_status != calculated_status: | |
|
957 | PullRequestModel()._trigger_pull_request_hook( | |
|
958 | pull_request, c.rhodecode_user, 'review_status_change') | |
|
959 | ||
|
960 | calculated_status_lbl = ChangesetStatus.get_status_lbl( | |
|
961 | calculated_status) | |
|
952 | Session().flush() | |
|
953 | events.trigger( | |
|
954 | events.PullRequestCommentEvent(pull_request, comment)) | |
|
962 | 955 | |
|
963 | if close_pr: | |
|
964 | status_completed = ( | |
|
965 | calculated_status in [ChangesetStatus.STATUS_APPROVED, | |
|
966 | ChangesetStatus.STATUS_REJECTED]) | |
|
967 | if close_pull_request or status_completed: | |
|
968 | PullRequestModel().close_pull_request( | |
|
969 | pull_request_id, c.rhodecode_user) | |
|
970 | else: | |
|
971 | h.flash(_('Closing pull request on other statuses than ' | |
|
972 | 'rejected or approved is forbidden. ' | |
|
973 | 'Calculated status from all reviewers ' | |
|
974 | 'is currently: %s') % calculated_status_lbl, | |
|
975 | category='warning') | |
|
956 | # we now calculate the status of pull request, and based on that | |
|
957 | # calculation we set the commits status | |
|
958 | calculated_status = pull_request.calculated_review_status() | |
|
959 | if old_calculated_status != calculated_status: | |
|
960 | PullRequestModel()._trigger_pull_request_hook( | |
|
961 | pull_request, c.rhodecode_user, 'review_status_change') | |
|
976 | 962 | |
|
977 | 963 | Session().commit() |
|
978 | 964 | |
@@ -983,12 +969,11 b' class PullrequestsController(BaseRepoCon' | |||
|
983 | 969 | data = { |
|
984 | 970 | 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))), |
|
985 | 971 | } |
|
986 | if comm: | |
|
987 | c.co = comm | |
|
988 | c.inline_comment = True if comm.line_no else False | |
|
989 | data.update(comm.get_dict()) | |
|
990 | data.update({'rendered_text': | |
|
991 | render('changeset/changeset_comment_block.mako')}) | |
|
972 | if comment: | |
|
973 | c.co = comment | |
|
974 | rendered_comment = render('changeset/changeset_comment_block.mako') | |
|
975 | data.update(comment.get_dict()) | |
|
976 | data.update({'rendered_text': rendered_comment}) | |
|
992 | 977 | |
|
993 | 978 | return data |
|
994 | 979 |
@@ -3107,6 +3107,10 b' class ChangesetComment(Base, BaseModel):' | |||
|
3107 | 3107 | def is_todo(self): |
|
3108 | 3108 | return self.comment_type == self.COMMENT_TYPE_TODO |
|
3109 | 3109 | |
|
3110 | @property | |
|
3111 | def is_inline(self): | |
|
3112 | return self.line_no and self.f_path | |
|
3113 | ||
|
3110 | 3114 | def get_index_version(self, versions): |
|
3111 | 3115 | return self.get_index_from_version( |
|
3112 | 3116 | self.pull_request_version_id, versions) |
@@ -34,6 +34,7 b' from pylons.i18n.translation import lazy' | |||
|
34 | 34 | from pyramid.threadlocal import get_current_request |
|
35 | 35 | from sqlalchemy import or_ |
|
36 | 36 | |
|
37 | from rhodecode import events | |
|
37 | 38 | from rhodecode.lib import helpers as h, hooks_utils, diffs |
|
38 | 39 | from rhodecode.lib.compat import OrderedDict |
|
39 | 40 | from rhodecode.lib.hooks_daemon import prepare_callback_daemon |
@@ -1064,42 +1065,61 b' class PullRequestModel(BaseModel):' | |||
|
1064 | 1065 | pull_request, pull_request.author, 'close') |
|
1065 | 1066 | self._log_action('user_closed_pull_request', user, pull_request) |
|
1066 | 1067 | |
|
1067 |
def close_pull_request_with_comment( |
|
|
1068 | message=None): | |
|
1069 | status = ChangesetStatus.STATUS_REJECTED | |
|
1068 | def close_pull_request_with_comment( | |
|
1069 | self, pull_request, user, repo, message=None): | |
|
1070 | ||
|
1071 | pull_request_review_status = pull_request.calculated_review_status() | |
|
1070 | 1072 | |
|
1071 | if not message: | |
|
1072 | message = ( | |
|
1073 | _('Status change %(transition_icon)s %(status)s') % { | |
|
1074 | 'transition_icon': '>', | |
|
1075 | 'status': ChangesetStatus.get_status_lbl(status)}) | |
|
1073 | if pull_request_review_status == ChangesetStatus.STATUS_APPROVED: | |
|
1074 | # approved only if we have voting consent | |
|
1075 | status = ChangesetStatus.STATUS_APPROVED | |
|
1076 | else: | |
|
1077 | status = ChangesetStatus.STATUS_REJECTED | |
|
1078 | status_lbl = ChangesetStatus.get_status_lbl(status) | |
|
1076 | 1079 | |
|
1077 | internal_message = _('Closing with') + ' ' + message | |
|
1080 | default_message = ( | |
|
1081 | _('Closing with status change {transition_icon} {status}.') | |
|
1082 | ).format(transition_icon='>', status=status_lbl) | |
|
1083 | text = message or default_message | |
|
1078 | 1084 | |
|
1079 | comm = CommentsModel().create( | |
|
1080 | text=internal_message, | |
|
1085 | # create a comment, and link it to new status | |
|
1086 | comment = CommentsModel().create( | |
|
1087 | text=text, | |
|
1081 | 1088 | repo=repo.repo_id, |
|
1082 | 1089 | user=user.user_id, |
|
1083 | 1090 | pull_request=pull_request.pull_request_id, |
|
1084 | f_path=None, | |
|
1085 | line_no=None, | |
|
1086 | status_change=ChangesetStatus.get_status_lbl(status), | |
|
1091 | status_change=status_lbl, | |
|
1087 | 1092 | status_change_type=status, |
|
1088 | 1093 | closing_pr=True |
|
1089 | 1094 | ) |
|
1090 | 1095 | |
|
1096 | # calculate old status before we change it | |
|
1097 | old_calculated_status = pull_request.calculated_review_status() | |
|
1091 | 1098 | ChangesetStatusModel().set_status( |
|
1092 | 1099 | repo.repo_id, |
|
1093 | 1100 | status, |
|
1094 | 1101 | user.user_id, |
|
1095 | comm, | |
|
1102 | comment=comment, | |
|
1096 | 1103 | pull_request=pull_request.pull_request_id |
|
1097 | 1104 | ) |
|
1105 | ||
|
1098 | 1106 | Session().flush() |
|
1107 | events.trigger(events.PullRequestCommentEvent(pull_request, comment)) | |
|
1108 | # we now calculate the status of pull request again, and based on that | |
|
1109 | # calculation trigger status change. This might happen in cases | |
|
1110 | # that non-reviewer admin closes a pr, which means his vote doesn't | |
|
1111 | # change the status, while if he's a reviewer this might change it. | |
|
1112 | calculated_status = pull_request.calculated_review_status() | |
|
1113 | if old_calculated_status != calculated_status: | |
|
1114 | self._trigger_pull_request_hook( | |
|
1115 | pull_request, user, 'review_status_change') | |
|
1099 | 1116 | |
|
1117 | # finally close the PR | |
|
1100 | 1118 | PullRequestModel().close_pull_request( |
|
1101 | 1119 | pull_request.pull_request_id, user) |
|
1102 | 1120 | |
|
1121 | return comment, status | |
|
1122 | ||
|
1103 | 1123 | def merge_status(self, pull_request): |
|
1104 | 1124 | if not self._is_merge_enabled(pull_request): |
|
1105 | 1125 | return False, _('Server-side pull request merging is disabled.') |
@@ -96,6 +96,7 b' function registerRCRoutes() {' | |||
|
96 | 96 | pyroutes.register('goto_switcher_data', '/_goto_data', []); |
|
97 | 97 | pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']); |
|
98 | 98 | pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']); |
|
99 | pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']); | |
|
99 | 100 | pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']); |
|
100 | 101 | pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']); |
|
101 | 102 | pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']); |
@@ -368,16 +368,6 b' var _updatePullRequest = function(repo_n' | |||
|
368 | 368 | }; |
|
369 | 369 | |
|
370 | 370 | /** |
|
371 | * PULL REQUEST reject & close | |
|
372 | */ | |
|
373 | var closePullRequest = function(repo_name, pull_request_id) { | |
|
374 | var postData = { | |
|
375 | '_method': 'put', | |
|
376 | 'close_pull_request': true}; | |
|
377 | _updatePullRequest(repo_name, pull_request_id, postData); | |
|
378 | }; | |
|
379 | ||
|
380 | /** | |
|
381 | 371 | * PULL REQUEST update commits |
|
382 | 372 | */ |
|
383 | 373 | var updateCommits = function(repo_name, pull_request_id) { |
@@ -1,4 +1,4 b'' | |||
|
1 | 1 | ## this is a dummy html file for partial rendering on server and sending |
|
2 | 2 | ## generated output via ajax after comment submit |
|
3 | 3 | <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/> |
|
4 |
${comment.comment_block(c.co, inline=c.inline |
|
|
4 | ${comment.comment_block(c.co, inline=c.co.is_inline)} |
@@ -949,9 +949,6 b'' | |||
|
949 | 949 | updateCommits("rhodecode-momentum", "720"); |
|
950 | 950 | }); |
|
951 | 951 | |
|
952 | $('#close_pull_request').on('click', function(e){ | |
|
953 | closePullRequest("rhodecode-momentum", "720"); | |
|
954 | }); | |
|
955 | 952 | }) |
|
956 | 953 | </script> |
|
957 | 954 |
@@ -825,11 +825,6 b'' | |||
|
825 | 825 | // fixing issue with caches on firefox |
|
826 | 826 | $('#update_commits').removeAttr("disabled"); |
|
827 | 827 | |
|
828 | $('#close_pull_request').on('click', function(e){ | |
|
829 | closePullRequest( | |
|
830 | "${c.repo_name}", "${c.pull_request.pull_request_id}"); | |
|
831 | }); | |
|
832 | ||
|
833 | 828 | $('.show-inline-comments').on('click', function(e){ |
|
834 | 829 | var boxid = $(this).attr('data-comment-id'); |
|
835 | 830 | var button = $(this); |
@@ -237,7 +237,9 b' class TestPullrequestsController(object)' | |||
|
237 | 237 | assertr.element_contains( |
|
238 | 238 | 'span[data-role="merge-message"]', str(expected_msg)) |
|
239 | 239 | |
|
240 |
def test_comment_and_close_pull_request( |
|
|
240 | def test_comment_and_close_pull_request_custom_message_approved( | |
|
241 | self, pr_util, csrf_token, xhr_header): | |
|
242 | ||
|
241 | 243 | pull_request = pr_util.create_pull_request(approved=True) |
|
242 | 244 | pull_request_id = pull_request.pull_request_id |
|
243 | 245 | author = pull_request.user_id |
@@ -249,11 +251,10 b' class TestPullrequestsController(object)' | |||
|
249 | 251 | repo_name=pull_request.target_repo.scm_instance().name, |
|
250 | 252 | pull_request_id=str(pull_request_id)), |
|
251 | 253 | params={ |
|
252 | 'changeset_status': ChangesetStatus.STATUS_APPROVED, | |
|
253 | 254 | 'close_pull_request': '1', |
|
254 | 255 | 'text': 'Closing a PR', |
|
255 | 256 | 'csrf_token': csrf_token}, |
|
256 | status=302) | |
|
257 | extra_environ=xhr_header,) | |
|
257 | 258 | |
|
258 | 259 | action = 'user_closed_pull_request:%d' % pull_request_id |
|
259 | 260 | journal = UserLog.query()\ |
@@ -270,45 +271,26 b' class TestPullrequestsController(object)' | |||
|
270 | 271 | status = ChangesetStatusModel().get_status( |
|
271 | 272 | pull_request.source_repo, pull_request=pull_request) |
|
272 | 273 | assert status == ChangesetStatus.STATUS_APPROVED |
|
273 | ||
|
274 | def test_reject_and_close_pull_request(self, pr_util, csrf_token): | |
|
275 | pull_request = pr_util.create_pull_request() | |
|
276 | pull_request_id = pull_request.pull_request_id | |
|
277 | response = self.app.post( | |
|
278 | url(controller='pullrequests', | |
|
279 | action='update', | |
|
280 | repo_name=pull_request.target_repo.scm_instance().name, | |
|
281 | pull_request_id=str(pull_request.pull_request_id)), | |
|
282 | params={'close_pull_request': 'true', '_method': 'put', | |
|
283 | 'csrf_token': csrf_token}) | |
|
274 | assert pull_request.comments[-1].text == 'Closing a PR' | |
|
284 | 275 | |
|
285 | pull_request = PullRequest.get(pull_request_id) | |
|
286 | ||
|
287 | assert response.json is True | |
|
288 | assert pull_request.is_closed() | |
|
289 | ||
|
290 | # check only the latest status, not the review status | |
|
291 | status = ChangesetStatusModel().get_status( | |
|
292 | pull_request.source_repo, pull_request=pull_request) | |
|
293 | assert status == ChangesetStatus.STATUS_REJECTED | |
|
294 | ||
|
295 | def test_comment_force_close_pull_request(self, pr_util, csrf_token): | |
|
276 | def test_comment_force_close_pull_request_rejected( | |
|
277 | self, pr_util, csrf_token, xhr_header): | |
|
296 | 278 | pull_request = pr_util.create_pull_request() |
|
297 | 279 | pull_request_id = pull_request.pull_request_id |
|
298 | 280 | PullRequestModel().update_reviewers( |
|
299 | 281 | pull_request_id, [(1, ['reason'], False), (2, ['reason2'], False)]) |
|
300 | 282 | author = pull_request.user_id |
|
301 | 283 | repo = pull_request.target_repo.repo_id |
|
284 | ||
|
302 | 285 | self.app.post( |
|
303 | 286 | url(controller='pullrequests', |
|
304 | 287 | action='comment', |
|
305 | 288 | repo_name=pull_request.target_repo.scm_instance().name, |
|
306 | 289 | pull_request_id=str(pull_request_id)), |
|
307 | 290 | params={ |
|
308 | 'changeset_status': 'rejected', | |
|
309 | 291 | 'close_pull_request': '1', |
|
310 | 292 | 'csrf_token': csrf_token}, |
|
311 | status=302) | |
|
293 | extra_environ=xhr_header) | |
|
312 | 294 | |
|
313 | 295 | pull_request = PullRequest.get(pull_request_id) |
|
314 | 296 | |
@@ -324,6 +306,31 b' class TestPullrequestsController(object)' | |||
|
324 | 306 | pull_request.source_repo, pull_request=pull_request) |
|
325 | 307 | assert status == ChangesetStatus.STATUS_REJECTED |
|
326 | 308 | |
|
309 | def test_comment_and_close_pull_request( | |
|
310 | self, pr_util, csrf_token, xhr_header): | |
|
311 | pull_request = pr_util.create_pull_request() | |
|
312 | pull_request_id = pull_request.pull_request_id | |
|
313 | ||
|
314 | response = self.app.post( | |
|
315 | url(controller='pullrequests', | |
|
316 | action='comment', | |
|
317 | repo_name=pull_request.target_repo.scm_instance().name, | |
|
318 | pull_request_id=str(pull_request.pull_request_id)), | |
|
319 | params={ | |
|
320 | 'close_pull_request': 'true', | |
|
321 | 'csrf_token': csrf_token}, | |
|
322 | extra_environ=xhr_header) | |
|
323 | ||
|
324 | assert response.json | |
|
325 | ||
|
326 | pull_request = PullRequest.get(pull_request_id) | |
|
327 | assert pull_request.is_closed() | |
|
328 | ||
|
329 | # check only the latest status, not the review status | |
|
330 | status = ChangesetStatusModel().get_status( | |
|
331 | pull_request.source_repo, pull_request=pull_request) | |
|
332 | assert status == ChangesetStatus.STATUS_REJECTED | |
|
333 | ||
|
327 | 334 | def test_create_pull_request(self, backend, csrf_token): |
|
328 | 335 | commits = [ |
|
329 | 336 | {'message': 'ancestor'}, |
General Comments 0
You need to be logged in to leave comments.
Login now