##// END OF EJS Templates
observers: code cleanups and fixed tests.
marcink -
r4519:ea50ffa9 stable
parent child
Show More
@@ -320,7 +320,7 class TestCreatePullRequestApi(object):
320 id_, params = build_data(
320 id_, params = build_data(
321 self.apikey_regular, 'create_pull_request', **data)
321 self.apikey_regular, 'create_pull_request', **data)
322 response = api_call(self.app, params)
322 response = api_call(self.app, params)
323 expected_message = 'no commits found'
323 expected_message = 'no commits found for merge between specified references'
324 assert_error(id_, expected_message, given=response.body)
324 assert_error(id_, expected_message, given=response.body)
325
325
326 @pytest.mark.backends("git", "hg")
326 @pytest.mark.backends("git", "hg")
@@ -29,6 +29,7 from rhodecode.api.tests.utils import (
29
29
30 @pytest.mark.usefixtures("testuser_api", "app")
30 @pytest.mark.usefixtures("testuser_api", "app")
31 class TestGetPullRequest(object):
31 class TestGetPullRequest(object):
32
32 @pytest.mark.backends("git", "hg")
33 @pytest.mark.backends("git", "hg")
33 def test_api_get_pull_requests(self, pr_util):
34 def test_api_get_pull_requests(self, pr_util):
34 pull_request = pr_util.create_pull_request()
35 pull_request = pr_util.create_pull_request()
@@ -40,6 +41,7 class TestGetPullRequest(object):
40 target_ref=pull_request.target_ref,
41 target_ref=pull_request.target_ref,
41 revisions=pull_request.revisions,
42 revisions=pull_request.revisions,
42 reviewers=(),
43 reviewers=(),
44 observers=(),
43 title=pull_request.title,
45 title=pull_request.title,
44 description=pull_request.description,
46 description=pull_request.description,
45 )
47 )
@@ -51,6 +51,7 class TestUpdatePullRequest(object):
51 "pull_request": response.json['result']['pull_request'],
51 "pull_request": response.json['result']['pull_request'],
52 "updated_commits": {"added": [], "common": [], "removed": []},
52 "updated_commits": {"added": [], "common": [], "removed": []},
53 "updated_reviewers": {"added": [], "removed": []},
53 "updated_reviewers": {"added": [], "removed": []},
54 "updated_observers": {"added": [], "removed": []},
54 }
55 }
55
56
56 response_json = response.json['result']
57 response_json = response.json['result']
@@ -111,6 +112,7 class TestUpdatePullRequest(object):
111 "total": total_commits,
112 "total": total_commits,
112 "removed": []},
113 "removed": []},
113 "updated_reviewers": {"added": [], "removed": []},
114 "updated_reviewers": {"added": [], "removed": []},
115 "updated_observers": {"added": [], "removed": []},
114 }
116 }
115
117
116 assert_ok(id_, expected, response.body)
118 assert_ok(id_, expected, response.body)
@@ -122,7 +124,7 class TestUpdatePullRequest(object):
122 b = user_util.create_user()
124 b = user_util.create_user()
123 c = user_util.create_user()
125 c = user_util.create_user()
124 new_reviewers = [
126 new_reviewers = [
125 {'username': b.username,'reasons': ['updated via API'],
127 {'username': b.username, 'reasons': ['updated via API'],
126 'mandatory':False},
128 'mandatory':False},
127 {'username': c.username, 'reasons': ['updated via API'],
129 {'username': c.username, 'reasons': ['updated via API'],
128 'mandatory':False},
130 'mandatory':False},
@@ -132,7 +134,7 class TestUpdatePullRequest(object):
132 removed = [a.username]
134 removed = [a.username]
133
135
134 pull_request = pr_util.create_pull_request(
136 pull_request = pr_util.create_pull_request(
135 reviewers=[(a.username, ['added via API'], False, [])])
137 reviewers=[(a.username, ['added via API'], False, 'reviewer', [])])
136
138
137 id_, params = build_data(
139 id_, params = build_data(
138 self.apikey, 'update_pull_request',
140 self.apikey, 'update_pull_request',
@@ -146,6 +148,7 class TestUpdatePullRequest(object):
146 "pull_request": response.json['result']['pull_request'],
148 "pull_request": response.json['result']['pull_request'],
147 "updated_commits": {"added": [], "common": [], "removed": []},
149 "updated_commits": {"added": [], "common": [], "removed": []},
148 "updated_reviewers": {"added": added, "removed": removed},
150 "updated_reviewers": {"added": added, "removed": removed},
151 "updated_observers": {"added": [], "removed": []},
149 }
152 }
150
153
151 assert_ok(id_, expected, response.body)
154 assert_ok(id_, expected, response.body)
@@ -30,9 +30,11 from rhodecode.lib import channelstream
30 from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
30 from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
31 from rhodecode.lib.base import vcs_operation_context
31 from rhodecode.lib.base import vcs_operation_context
32 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.lib.utils2 import str2bool
33 from rhodecode.lib.vcs.backends.base import unicode_to_reference
33 from rhodecode.model.changeset_status import ChangesetStatusModel
34 from rhodecode.model.changeset_status import ChangesetStatusModel
34 from rhodecode.model.comment import CommentsModel
35 from rhodecode.model.comment import CommentsModel
35 from rhodecode.model.db import Session, ChangesetStatus, ChangesetComment, PullRequest
36 from rhodecode.model.db import (
37 Session, ChangesetStatus, ChangesetComment, PullRequest, PullRequestReviewers)
36 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
38 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
37 from rhodecode.model.settings import SettingsModel
39 from rhodecode.model.settings import SettingsModel
38 from rhodecode.model.validation_schema import Invalid
40 from rhodecode.model.validation_schema import Invalid
@@ -600,7 +602,7 def comment_pull_request(
600 extra_recipients=extra_recipients,
602 extra_recipients=extra_recipients,
601 send_email=send_email
603 send_email=send_email
602 )
604 )
603 is_inline = bool(comment.f_path and comment.line_no)
605 is_inline = comment.is_inline
604
606
605 if allowed_to_change_status and status:
607 if allowed_to_change_status and status:
606 old_calculated_status = pull_request.calculated_review_status()
608 old_calculated_status = pull_request.calculated_review_status()
@@ -646,12 +648,26 def comment_pull_request(
646
648
647 return data
649 return data
648
650
651 def _reviewers_validation(obj_list):
652 schema = ReviewerListSchema()
653 try:
654 reviewer_objects = schema.deserialize(obj_list)
655 except Invalid as err:
656 raise JSONRPCValidationError(colander_exc=err)
657
658 # validate users
659 for reviewer_object in reviewer_objects:
660 user = get_user_or_error(reviewer_object['username'])
661 reviewer_object['user_id'] = user.user_id
662 return reviewer_objects
663
649
664
650 @jsonrpc_method()
665 @jsonrpc_method()
651 def create_pull_request(
666 def create_pull_request(
652 request, apiuser, source_repo, target_repo, source_ref, target_ref,
667 request, apiuser, source_repo, target_repo, source_ref, target_ref,
653 owner=Optional(OAttr('apiuser')), title=Optional(''), description=Optional(''),
668 owner=Optional(OAttr('apiuser')), title=Optional(''), description=Optional(''),
654 description_renderer=Optional(''), reviewers=Optional(None)):
669 description_renderer=Optional(''),
670 reviewers=Optional(None), observers=Optional(None)):
655 """
671 """
656 Creates a new pull request.
672 Creates a new pull request.
657
673
@@ -689,6 +705,13 def create_pull_request(
689 Accepts username strings or objects of the format:
705 Accepts username strings or objects of the format:
690
706
691 [{'username': 'nick', 'reasons': ['original author'], 'mandatory': <bool>}]
707 [{'username': 'nick', 'reasons': ['original author'], 'mandatory': <bool>}]
708 :param observers: Set the new pull request observers list.
709 Reviewer defined by review rules will be added automatically to the
710 defined list. This feature is only available in RhodeCode EE
711 :type observers: Optional(list)
712 Accepts username strings or objects of the format:
713
714 [{'username': 'nick', 'reasons': ['original author']}]
692 """
715 """
693
716
694 source_db_repo = get_repo_or_error(source_repo)
717 source_db_repo = get_repo_or_error(source_repo)
@@ -702,38 +725,39 def create_pull_request(
702 full_source_ref = resolve_ref_or_error(source_ref, source_db_repo)
725 full_source_ref = resolve_ref_or_error(source_ref, source_db_repo)
703 full_target_ref = resolve_ref_or_error(target_ref, target_db_repo)
726 full_target_ref = resolve_ref_or_error(target_ref, target_db_repo)
704
727
705 source_commit = get_commit_or_error(full_source_ref, source_db_repo)
728 get_commit_or_error(full_source_ref, source_db_repo)
706 target_commit = get_commit_or_error(full_target_ref, target_db_repo)
729 get_commit_or_error(full_target_ref, target_db_repo)
707
730
708 reviewer_objects = Optional.extract(reviewers) or []
731 reviewer_objects = Optional.extract(reviewers) or []
732 observer_objects = Optional.extract(observers) or []
709
733
710 # serialize and validate passed in given reviewers
734 # serialize and validate passed in given reviewers
711 if reviewer_objects:
735 if reviewer_objects:
712 schema = ReviewerListSchema()
736 reviewer_objects = _reviewers_validation(reviewer_objects)
713 try:
714 reviewer_objects = schema.deserialize(reviewer_objects)
715 except Invalid as err:
716 raise JSONRPCValidationError(colander_exc=err)
717
737
718 # validate users
738 if observer_objects:
719 for reviewer_object in reviewer_objects:
739 observer_objects = _reviewers_validation(reviewer_objects)
720 user = get_user_or_error(reviewer_object['username'])
721 reviewer_object['user_id'] = user.user_id
722
740
723 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
741 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
724 PullRequestModel().get_reviewer_functions()
742 PullRequestModel().get_reviewer_functions()
725
743
744 source_ref_obj = unicode_to_reference(full_source_ref)
745 target_ref_obj = unicode_to_reference(full_target_ref)
746
726 # recalculate reviewers logic, to make sure we can validate this
747 # recalculate reviewers logic, to make sure we can validate this
727 default_reviewers_data = get_default_reviewers_data(
748 default_reviewers_data = get_default_reviewers_data(
728 owner,
749 owner,
729 source_repo,
750 source_db_repo,
730 Reference(source_type, source_name, source_commit_id),
751 source_ref_obj,
731 target_repo,
752 target_db_repo,
732 Reference(target_type, target_name, target_commit_id)
753 target_ref_obj,
733 )
754 )
734
755
735 # now MERGE our given with the calculated
756 # now MERGE our given with the calculated from the default rules
736 reviewer_objects = default_reviewers_data['reviewers'] + reviewer_objects
757 just_reviewers = [
758 x for x in default_reviewers_data['reviewers']
759 if x['role'] == PullRequestReviewers.ROLE_REVIEWER]
760 reviewer_objects = just_reviewers + reviewer_objects
737
761
738 try:
762 try:
739 reviewers = validate_default_reviewers(
763 reviewers = validate_default_reviewers(
@@ -741,9 +765,21 def create_pull_request(
741 except ValueError as e:
765 except ValueError as e:
742 raise JSONRPCError('Reviewers Validation: {}'.format(e))
766 raise JSONRPCError('Reviewers Validation: {}'.format(e))
743
767
768 # now MERGE our given with the calculated from the default rules
769 just_observers = [
770 x for x in default_reviewers_data['reviewers']
771 if x['role'] == PullRequestReviewers.ROLE_OBSERVER]
772 observer_objects = just_observers + observer_objects
773
774 try:
775 observers = validate_observers(
776 observer_objects, default_reviewers_data)
777 except ValueError as e:
778 raise JSONRPCError('Observer Validation: {}'.format(e))
779
744 title = Optional.extract(title)
780 title = Optional.extract(title)
745 if not title:
781 if not title:
746 title_source_ref = source_ref.split(':', 2)[1]
782 title_source_ref = source_ref_obj.name
747 title = PullRequestModel().generate_pullrequest_title(
783 title = PullRequestModel().generate_pullrequest_title(
748 source=source_repo,
784 source=source_repo,
749 source_ref=title_source_ref,
785 source_ref=title_source_ref,
@@ -752,20 +788,17 def create_pull_request(
752
788
753 diff_info = default_reviewers_data['diff_info']
789 diff_info = default_reviewers_data['diff_info']
754 common_ancestor_id = diff_info['ancestor']
790 common_ancestor_id = diff_info['ancestor']
755 commits = diff_info['commits']
791 # NOTE(marcink): reversed is consistent with how we open it in the WEB interface
792 commits = [commit['commit_id'] for commit in reversed(diff_info['commits'])]
756
793
757 if not common_ancestor_id:
794 if not common_ancestor_id:
758 raise JSONRPCError('no common ancestor found')
795 raise JSONRPCError('no common ancestor found between specified references')
759
796
760 if not commits:
797 if not commits:
761 raise JSONRPCError('no commits found')
798 raise JSONRPCError('no commits found for merge between specified references')
762
763 # NOTE(marcink): reversed is consistent with how we open it in the WEB interface
764 revisions = [commit.raw_id for commit in reversed(commits)]
765
799
766 # recalculate target ref based on ancestor
800 # recalculate target ref based on ancestor
767 target_ref_type, target_ref_name, __ = full_target_ref.split(':')
801 full_target_ref = ':'.join((target_ref_obj.type, target_ref_obj.name, common_ancestor_id))
768 full_target_ref = ':'.join((target_ref_type, target_ref_name, common_ancestor_id))
769
802
770 # fetch renderer, if set fallback to plain in case of PR
803 # fetch renderer, if set fallback to plain in case of PR
771 rc_config = SettingsModel().get_all_settings()
804 rc_config = SettingsModel().get_all_settings()
@@ -780,8 +813,9 def create_pull_request(
780 target_repo=target_repo,
813 target_repo=target_repo,
781 target_ref=full_target_ref,
814 target_ref=full_target_ref,
782 common_ancestor_id=common_ancestor_id,
815 common_ancestor_id=common_ancestor_id,
783 revisions=revisions,
816 revisions=commits,
784 reviewers=reviewers,
817 reviewers=reviewers,
818 observers=observers,
785 title=title,
819 title=title,
786 description=description,
820 description=description,
787 description_renderer=description_renderer,
821 description_renderer=description_renderer,
@@ -801,7 +835,7 def create_pull_request(
801 def update_pull_request(
835 def update_pull_request(
802 request, apiuser, pullrequestid, repoid=Optional(None),
836 request, apiuser, pullrequestid, repoid=Optional(None),
803 title=Optional(''), description=Optional(''), description_renderer=Optional(''),
837 title=Optional(''), description=Optional(''), description_renderer=Optional(''),
804 reviewers=Optional(None), update_commits=Optional(None)):
838 reviewers=Optional(None), observers=Optional(None), update_commits=Optional(None)):
805 """
839 """
806 Updates a pull request.
840 Updates a pull request.
807
841
@@ -823,7 +857,11 def update_pull_request(
823 Accepts username strings or objects of the format:
857 Accepts username strings or objects of the format:
824
858
825 [{'username': 'nick', 'reasons': ['original author'], 'mandatory': <bool>}]
859 [{'username': 'nick', 'reasons': ['original author'], 'mandatory': <bool>}]
860 :param observers: Update pull request observers list with new value.
861 :type observers: Optional(list)
862 Accepts username strings or objects of the format:
826
863
864 [{'username': 'nick', 'reasons': ['should be aware about this PR']}]
827 :param update_commits: Trigger update of commits for this pull request
865 :param update_commits: Trigger update of commits for this pull request
828 :type: update_commits: Optional(bool)
866 :type: update_commits: Optional(bool)
829
867
@@ -841,6 +879,12 def update_pull_request(
841 ],
879 ],
842 "removed": []
880 "removed": []
843 },
881 },
882 "updated_observers": {
883 "added": [
884 "username"
885 ],
886 "removed": []
887 },
844 "updated_commits": {
888 "updated_commits": {
845 "added": [
889 "added": [
846 "<sha1_hash>"
890 "<sha1_hash>"
@@ -872,30 +916,7 def update_pull_request(
872 pullrequestid,))
916 pullrequestid,))
873
917
874 reviewer_objects = Optional.extract(reviewers) or []
918 reviewer_objects = Optional.extract(reviewers) or []
875
919 observer_objects = Optional.extract(observers) or []
876 if reviewer_objects:
877 schema = ReviewerListSchema()
878 try:
879 reviewer_objects = schema.deserialize(reviewer_objects)
880 except Invalid as err:
881 raise JSONRPCValidationError(colander_exc=err)
882
883 # validate users
884 for reviewer_object in reviewer_objects:
885 user = get_user_or_error(reviewer_object['username'])
886 reviewer_object['user_id'] = user.user_id
887
888 get_default_reviewers_data, get_validated_reviewers, validate_observers = \
889 PullRequestModel().get_reviewer_functions()
890
891 # re-use stored rules
892 reviewer_rules = pull_request.reviewer_data
893 try:
894 reviewers = get_validated_reviewers(reviewer_objects, reviewer_rules)
895 except ValueError as e:
896 raise JSONRPCError('Reviewers Validation: {}'.format(e))
897 else:
898 reviewers = []
899
920
900 title = Optional.extract(title)
921 title = Optional.extract(title)
901 description = Optional.extract(description)
922 description = Optional.extract(description)
@@ -935,12 +956,41 def update_pull_request(
935 commits_changed = True
956 commits_changed = True
936
957
937 # Update reviewers
958 # Update reviewers
959 # serialize and validate passed in given reviewers
960 if reviewer_objects:
961 reviewer_objects = _reviewers_validation(reviewer_objects)
962
963 if observer_objects:
964 observer_objects = _reviewers_validation(reviewer_objects)
965
966 # re-use stored rules
967 default_reviewers_data = pull_request.reviewer_data
968
969 __, validate_default_reviewers, validate_observers = \
970 PullRequestModel().get_reviewer_functions()
971
972 if reviewer_objects:
973 try:
974 reviewers = validate_default_reviewers(reviewer_objects, default_reviewers_data)
975 except ValueError as e:
976 raise JSONRPCError('Reviewers Validation: {}'.format(e))
977 else:
978 reviewers = []
979
980 if observer_objects:
981 try:
982 observers = validate_default_reviewers(reviewer_objects, default_reviewers_data)
983 except ValueError as e:
984 raise JSONRPCError('Observer Validation: {}'.format(e))
985 else:
986 observers = []
987
938 reviewers_changed = False
988 reviewers_changed = False
939 reviewers_changes = {"added": [], "removed": []}
989 reviewers_changes = {"added": [], "removed": []}
940 if reviewers:
990 if reviewers:
941 old_calculated_status = pull_request.calculated_review_status()
991 old_calculated_status = pull_request.calculated_review_status()
942 added_reviewers, removed_reviewers = \
992 added_reviewers, removed_reviewers = \
943 PullRequestModel().update_reviewers(pull_request, reviewers, apiuser)
993 PullRequestModel().update_reviewers(pull_request, reviewers, apiuser.get_instance())
944
994
945 reviewers_changes['added'] = sorted(
995 reviewers_changes['added'] = sorted(
946 [get_user_or_error(n).username for n in added_reviewers])
996 [get_user_or_error(n).username for n in added_reviewers])
@@ -957,6 +1007,18 def update_pull_request(
957 reviewers_changed = True
1007 reviewers_changed = True
958
1008
959 observers_changed = False
1009 observers_changed = False
1010 observers_changes = {"added": [], "removed": []}
1011 if observers:
1012 added_observers, removed_observers = \
1013 PullRequestModel().update_observers(pull_request, observers, apiuser.get_instance())
1014
1015 observers_changes['added'] = sorted(
1016 [get_user_or_error(n).username for n in added_observers])
1017 observers_changes['removed'] = sorted(
1018 [get_user_or_error(n).username for n in removed_observers])
1019 Session().commit()
1020
1021 reviewers_changed = True
960
1022
961 # push changed to channelstream
1023 # push changed to channelstream
962 if commits_changed or reviewers_changed or observers_changed:
1024 if commits_changed or reviewers_changed or observers_changed:
@@ -966,11 +1028,11 def update_pull_request(
966 request, pr_broadcast_channel, apiuser, msg)
1028 request, pr_broadcast_channel, apiuser, msg)
967
1029
968 data = {
1030 data = {
969 'msg': 'Updated pull request `{}`'.format(
1031 'msg': 'Updated pull request `{}`'.format(pull_request.pull_request_id),
970 pull_request.pull_request_id),
971 'pull_request': pull_request.get_api_data(),
1032 'pull_request': pull_request.get_api_data(),
972 'updated_commits': commit_changes,
1033 'updated_commits': commit_changes,
973 'updated_reviewers': reviewers_changes
1034 'updated_reviewers': reviewers_changes,
1035 'updated_observers': observers_changes,
974 }
1036 }
975
1037
976 return data
1038 return data
@@ -1653,7 +1653,7 def comment_commit(
1653 extra_recipients=extra_recipients,
1653 extra_recipients=extra_recipients,
1654 send_email=send_email
1654 send_email=send_email
1655 )
1655 )
1656 is_inline = bool(comment.f_path and comment.line_no)
1656 is_inline = comment.is_inline
1657
1657
1658 if status:
1658 if status:
1659 # also do a status change
1659 # also do a status change
@@ -523,7 +523,9 class TestPullrequestsView(object):
523 pull_request = pr_util.create_pull_request()
523 pull_request = pr_util.create_pull_request()
524 pull_request_id = pull_request.pull_request_id
524 pull_request_id = pull_request.pull_request_id
525 PullRequestModel().update_reviewers(
525 PullRequestModel().update_reviewers(
526 pull_request_id, [(1, ['reason'], False, []), (2, ['reason2'], False, [])],
526 pull_request_id, [
527 (1, ['reason'], False, 'reviewer', []),
528 (2, ['reason2'], False, 'reviewer', [])],
527 pull_request.author)
529 pull_request.author)
528 author = pull_request.user_id
530 author = pull_request.user_id
529 repo = pull_request.target_repo.repo_id
531 repo = pull_request.target_repo.repo_id
@@ -906,12 +908,13 class TestPullrequestsView(object):
906
908
907 # Change reviewers and check that a notification was made
909 # Change reviewers and check that a notification was made
908 PullRequestModel().update_reviewers(
910 PullRequestModel().update_reviewers(
909 pull_request.pull_request_id, [(1, [], False, [])],
911 pull_request.pull_request_id, [
912 (1, [], False, 'reviewer', [])
913 ],
910 pull_request.author)
914 pull_request.author)
911 assert len(notifications.all()) == 2
915 assert len(notifications.all()) == 2
912
916
913 def test_create_pull_request_stores_ancestor_commit_id(self, backend,
917 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
914 csrf_token):
915 commits = [
918 commits = [
916 {'message': 'ancestor',
919 {'message': 'ancestor',
917 'added': [FileNode('file_A', content='content_of_ancestor')]},
920 'added': [FileNode('file_A', content='content_of_ancestor')]},
@@ -413,7 +413,7 class RepoCommitsView(RepoAppView):
413 resolves_comment_id=resolves_comment_id,
413 resolves_comment_id=resolves_comment_id,
414 auth_user=self._rhodecode_user
414 auth_user=self._rhodecode_user
415 )
415 )
416 is_inline = bool(comment.f_path and comment.line_no)
416 is_inline = comment.is_inline
417
417
418 # get status if set !
418 # get status if set !
419 if status:
419 if status:
@@ -40,7 +40,8 from rhodecode.lib.auth import (
40 LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator,
40 LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator,
41 NotAnonymous, CSRFRequired)
41 NotAnonymous, CSRFRequired)
42 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int, aslist
42 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int, aslist
43 from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason, Reference
43 from rhodecode.lib.vcs.backends.base import (
44 EmptyCommit, UpdateFailureReason, unicode_to_reference)
44 from rhodecode.lib.vcs.exceptions import (
45 from rhodecode.lib.vcs.exceptions import (
45 CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
46 CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
46 from rhodecode.model.changeset_status import ChangesetStatusModel
47 from rhodecode.model.changeset_status import ChangesetStatusModel
@@ -1146,16 +1147,17 class RepoPullRequestsView(RepoAppView,
1146 source_scm = source_db_repo.scm_instance()
1147 source_scm = source_db_repo.scm_instance()
1147 target_scm = target_db_repo.scm_instance()
1148 target_scm = target_db_repo.scm_instance()
1148
1149
1149 source_commit = source_scm.get_commit(source_ref.split(':')[-1])
1150 source_ref_obj = unicode_to_reference(source_ref)
1150 target_commit = target_scm.get_commit(target_ref.split(':')[-1])
1151 target_ref_obj = unicode_to_reference(target_ref)
1152
1153 source_commit = source_scm.get_commit(source_ref_obj.commit_id)
1154 target_commit = target_scm.get_commit(target_ref_obj.commit_id)
1151
1155
1152 ancestor = source_scm.get_common_ancestor(
1156 ancestor = source_scm.get_common_ancestor(
1153 source_commit.raw_id, target_commit.raw_id, target_scm)
1157 source_commit.raw_id, target_commit.raw_id, target_scm)
1154
1158
1155 source_ref_type, source_ref_name, source_commit_id = _form['target_ref'].split(':')
1156 target_ref_type, target_ref_name, target_commit_id = _form['source_ref'].split(':')
1157 # recalculate target ref based on ancestor
1159 # recalculate target ref based on ancestor
1158 target_ref = ':'.join((target_ref_type, target_ref_name, ancestor))
1160 target_ref = ':'.join((target_ref_obj.type, target_ref_obj.name, ancestor))
1159
1161
1160 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1162 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1161 PullRequestModel().get_reviewer_functions()
1163 PullRequestModel().get_reviewer_functions()
@@ -1164,16 +1166,16 class RepoPullRequestsView(RepoAppView,
1164 reviewer_rules = get_default_reviewers_data(
1166 reviewer_rules = get_default_reviewers_data(
1165 self._rhodecode_db_user,
1167 self._rhodecode_db_user,
1166 source_db_repo,
1168 source_db_repo,
1167 Reference(source_ref_type, source_ref_name, source_commit_id),
1169 source_ref_obj,
1168 target_db_repo,
1170 target_db_repo,
1169 Reference(target_ref_type, target_ref_name, target_commit_id),
1171 target_ref_obj,
1170 include_diff_info=False)
1172 include_diff_info=False)
1171
1173
1172 reviewers = validate_default_reviewers(_form['review_members'], reviewer_rules)
1174 reviewers = validate_default_reviewers(_form['review_members'], reviewer_rules)
1173 observers = validate_observers(_form['observer_members'], reviewer_rules)
1175 observers = validate_observers(_form['observer_members'], reviewer_rules)
1174
1176
1175 pullrequest_title = _form['pullrequest_title']
1177 pullrequest_title = _form['pullrequest_title']
1176 title_source_ref = source_ref.split(':', 2)[1]
1178 title_source_ref = source_ref_obj.name
1177 if not pullrequest_title:
1179 if not pullrequest_title:
1178 pullrequest_title = PullRequestModel().generate_pullrequest_title(
1180 pullrequest_title = PullRequestModel().generate_pullrequest_title(
1179 source=source_repo,
1181 source=source_repo,
@@ -1371,7 +1373,7 class RepoPullRequestsView(RepoAppView,
1371
1373
1372 old_calculated_status = pull_request.calculated_review_status()
1374 old_calculated_status = pull_request.calculated_review_status()
1373 PullRequestModel().update_reviewers(
1375 PullRequestModel().update_reviewers(
1374 pull_request, reviewers, self._rhodecode_user)
1376 pull_request, reviewers, self._rhodecode_db_user)
1375
1377
1376 Session().commit()
1378 Session().commit()
1377
1379
@@ -1396,7 +1398,7 class RepoPullRequestsView(RepoAppView,
1396 return
1398 return
1397
1399
1398 PullRequestModel().update_observers(
1400 PullRequestModel().update_observers(
1399 pull_request, observers, self._rhodecode_user)
1401 pull_request, observers, self._rhodecode_db_user)
1400
1402
1401 Session().commit()
1403 Session().commit()
1402 msg = _('Pull request observers updated.')
1404 msg = _('Pull request observers updated.')
@@ -1563,6 +1565,7 class RepoPullRequestsView(RepoAppView,
1563 pull_request, self._rhodecode_user, self.db_repo, message=text,
1565 pull_request, self._rhodecode_user, self.db_repo, message=text,
1564 auth_user=self._rhodecode_user)
1566 auth_user=self._rhodecode_user)
1565 Session().flush()
1567 Session().flush()
1568 is_inline = comment.is_inline
1566
1569
1567 PullRequestModel().trigger_pull_request_hook(
1570 PullRequestModel().trigger_pull_request_hook(
1568 pull_request, self._rhodecode_user, 'comment',
1571 pull_request, self._rhodecode_user, 'comment',
@@ -1596,7 +1599,7 class RepoPullRequestsView(RepoAppView,
1596 resolves_comment_id=resolves_comment_id,
1599 resolves_comment_id=resolves_comment_id,
1597 auth_user=self._rhodecode_user
1600 auth_user=self._rhodecode_user
1598 )
1601 )
1599 is_inline = bool(comment.f_path and comment.line_no)
1602 is_inline = comment.is_inline
1600
1603
1601 if allowed_to_change_status:
1604 if allowed_to_change_status:
1602 # calculate old status before we change it
1605 # calculate old status before we change it
@@ -73,6 +73,29 class Reference(_Reference):
73 return self.name
73 return self.name
74
74
75
75
76 def unicode_to_reference(raw):
77 """
78 Convert a unicode (or string) to a reference object.
79 If unicode evaluates to False it returns None.
80 """
81 if raw:
82 refs = raw.split(':')
83 return Reference(*refs)
84 else:
85 return None
86
87
88 def reference_to_unicode(ref):
89 """
90 Convert a reference object to unicode.
91 If reference is None it returns None.
92 """
93 if ref:
94 return u':'.join(ref)
95 else:
96 return None
97
98
76 class MergeFailureReason(object):
99 class MergeFailureReason(object):
77 """
100 """
78 Enumeration with all the reasons why the server side merge could fail.
101 Enumeration with all the reasons why the server side merge could fail.
@@ -56,7 +56,8 from webhelpers2.text import remove_form
56
56
57 from rhodecode.translation import _
57 from rhodecode.translation import _
58 from rhodecode.lib.vcs import get_vcs_instance, VCSError
58 from rhodecode.lib.vcs import get_vcs_instance, VCSError
59 from rhodecode.lib.vcs.backends.base import EmptyCommit, Reference
59 from rhodecode.lib.vcs.backends.base import (
60 EmptyCommit, Reference, unicode_to_reference, reference_to_unicode)
60 from rhodecode.lib.utils2 import (
61 from rhodecode.lib.utils2 import (
61 str2bool, safe_str, get_commit_safe, safe_unicode, sha1_safe,
62 str2bool, safe_str, get_commit_safe, safe_unicode, sha1_safe,
62 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict,
63 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict,
@@ -4248,26 +4249,11 class _PullRequestBase(BaseModel):
4248
4249
4249 @staticmethod
4250 @staticmethod
4250 def unicode_to_reference(raw):
4251 def unicode_to_reference(raw):
4251 """
4252 return unicode_to_reference(raw)
4252 Convert a unicode (or string) to a reference object.
4253 If unicode evaluates to False it returns None.
4254 """
4255 if raw:
4256 refs = raw.split(':')
4257 return Reference(*refs)
4258 else:
4259 return None
4260
4253
4261 @staticmethod
4254 @staticmethod
4262 def reference_to_unicode(ref):
4255 def reference_to_unicode(ref):
4263 """
4256 return reference_to_unicode(ref)
4264 Convert a reference object to unicode.
4265 If reference is None it returns None.
4266 """
4267 if ref:
4268 return u':'.join(ref)
4269 else:
4270 return None
4271
4257
4272 def get_api_data(self, with_merge_state=True):
4258 def get_api_data(self, with_merge_state=True):
4273 from rhodecode.model.pull_request import PullRequestModel
4259 from rhodecode.model.pull_request import PullRequestModel
@@ -4543,6 +4529,9 class PullRequestVersion(Base, _PullRequ
4543 @property
4529 @property
4544 def reviewers(self):
4530 def reviewers(self):
4545 return self.pull_request.reviewers
4531 return self.pull_request.reviewers
4532 @property
4533 def reviewers(self):
4534 return self.pull_request.reviewers
4546
4535
4547 @property
4536 @property
4548 def versions(self):
4537 def versions(self):
@@ -4561,7 +4550,7 class PullRequestVersion(Base, _PullRequ
4561 def reviewers_statuses(self):
4550 def reviewers_statuses(self):
4562 return self.pull_request.reviewers_statuses()
4551 return self.pull_request.reviewers_statuses()
4563
4552
4564 def observer(self):
4553 def observers(self):
4565 return self.pull_request.observers()
4554 return self.pull_request.observers()
4566
4555
4567
4556
@@ -1342,7 +1342,7 class PullRequestModel(BaseModel):
1342 'repo.pull_request.reviewer.delete', {'old_data': user_data},
1342 'repo.pull_request.reviewer.delete', {'old_data': user_data},
1343 user, pull_request)
1343 user, pull_request)
1344
1344
1345 self.notify_reviewers(pull_request, ids_to_add, user.get_instance())
1345 self.notify_reviewers(pull_request, ids_to_add, user)
1346 return ids_to_add, ids_to_remove
1346 return ids_to_add, ids_to_remove
1347
1347
1348 def update_observers(self, pull_request, observer_data, user):
1348 def update_observers(self, pull_request, observer_data, user):
@@ -1425,7 +1425,7 class PullRequestModel(BaseModel):
1425 'repo.pull_request.observer.delete', {'old_data': user_data},
1425 'repo.pull_request.observer.delete', {'old_data': user_data},
1426 user, pull_request)
1426 user, pull_request)
1427
1427
1428 self.notify_observers(pull_request, ids_to_add, user.get_instance())
1428 self.notify_observers(pull_request, ids_to_add, user)
1429 return ids_to_add, ids_to_remove
1429 return ids_to_add, ids_to_remove
1430
1430
1431 def get_url(self, pull_request, request=None, permalink=False):
1431 def get_url(self, pull_request, request=None, permalink=False):
@@ -21,12 +21,17
21 import colander
21 import colander
22 from rhodecode.model.validation_schema import validators, preparers, types
22 from rhodecode.model.validation_schema import validators, preparers, types
23
23
24 DEFAULT_ROLE = 'reviewer'
25 VALID_ROLES = ['reviewer', 'observer']
26
24
27
25 class ReviewerSchema(colander.MappingSchema):
28 class ReviewerSchema(colander.MappingSchema):
26 username = colander.SchemaNode(types.StrOrIntType())
29 username = colander.SchemaNode(types.StrOrIntType())
27 reasons = colander.SchemaNode(colander.List(), missing=['no reason specified'])
30 reasons = colander.SchemaNode(colander.List(), missing=['no reason specified'])
28 mandatory = colander.SchemaNode(colander.Boolean(), missing=False)
31 mandatory = colander.SchemaNode(colander.Boolean(), missing=False)
29 rules = colander.SchemaNode(colander.List(), missing=[])
32 rules = colander.SchemaNode(colander.List(), missing=[])
33 role = colander.SchemaNode(colander.String(), missing=DEFAULT_ROLE,
34 validator=colander.OneOf(VALID_ROLES))
30
35
31
36
32 class ReviewerListSchema(colander.SequenceSchema):
37 class ReviewerListSchema(colander.SequenceSchema):
@@ -97,6 +97,7
97 <li>The server is being restarted.</li>
97 <li>The server is being restarted.</li>
98 <li>The server is overloaded.</li>
98 <li>The server is overloaded.</li>
99 <li>The link may be incorrect.</li>
99 <li>The link may be incorrect.</li>
100 <li><a onclick="window.location.reload()">Reload page</a></li>
100 </ul>
101 </ul>
101 </div>
102 </div>
102 <div class="inner-column">
103 <div class="inner-column">
@@ -122,7 +122,7 class TestPullRequestModel(object):
122
122
123 def test_get_awaiting_my_review(self, pull_request):
123 def test_get_awaiting_my_review(self, pull_request):
124 PullRequestModel().update_reviewers(
124 PullRequestModel().update_reviewers(
125 pull_request, [(pull_request.author, ['author'], False, [])],
125 pull_request, [(pull_request.author, ['author'], False, 'reviewer', [])],
126 pull_request.author)
126 pull_request.author)
127 Session().commit()
127 Session().commit()
128
128
@@ -133,7 +133,7 class TestPullRequestModel(object):
133
133
134 def test_count_awaiting_my_review(self, pull_request):
134 def test_count_awaiting_my_review(self, pull_request):
135 PullRequestModel().update_reviewers(
135 PullRequestModel().update_reviewers(
136 pull_request, [(pull_request.author, ['author'], False, [])],
136 pull_request, [(pull_request.author, ['author'], False, 'reviewer', [])],
137 pull_request.author)
137 pull_request.author)
138 Session().commit()
138 Session().commit()
139
139
@@ -43,8 +43,8 from rhodecode.lib.utils2 import Attribu
43 from rhodecode.model.changeset_status import ChangesetStatusModel
43 from rhodecode.model.changeset_status import ChangesetStatusModel
44 from rhodecode.model.comment import CommentsModel
44 from rhodecode.model.comment import CommentsModel
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 PullRequest, Repository, RhodeCodeSetting, ChangesetStatus, RepoGroup,
46 PullRequest, PullRequestReviewers, Repository, RhodeCodeSetting, ChangesetStatus,
47 UserGroup, RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi)
47 RepoGroup, UserGroup, RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi)
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session