def test_comment_force_close_pull_request(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id
        reviewers_ids = [1, 2]
        PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
        author = pull_request.user_id
        repo = pull_request.target_repo.repo_id
        self.app.post(
            url(controller='pullrequests',
                action='comment',
                repo_name=pull_request.target_repo.scm_instance().name,
                pull_request_id=str(pull_request_id)),
            params={
                'changeset_status': 'forced_closed',
                'csrf_token': csrf_token},
            status=302)

        pull_request = PullRequest.get(pull_request_id)

        action = 'user_closed_pull_request:%d' % pull_request_id
        journal = UserLog.query().filter(
            UserLog.user_id == author,
            UserLog.repository_id == repo,
            UserLog.action == action).all()
        assert len(journal) == 1

        # check only the latest status, not the review status
        status = ChangesetStatusModel().get_status(
            pull_request.source_repo, pull_request=pull_request)
        assert status == ChangesetStatus.STATUS_REJECTED
예제 #2
0
    def merge(self, repo_name, pull_request_id):
        """
        POST /{repo_name}/pull-request/{pull_request_id}

        Merge will perform a server-side merge of the specified
        pull request, if the pull request is approved and mergeable.
        After succesfull merging, the pull request is automatically
        closed, with a relevant comment.
        """
        pull_request_id = safe_int(pull_request_id)
        pull_request = PullRequest.get_or_404(pull_request_id)
        user = c.rhodecode_user

        if self._meets_merge_pre_conditions(pull_request, user):
            log.debug("Pre-conditions checked, trying to merge.")
            extras = vcs_operation_context(
                request.environ,
                repo_name=pull_request.target_repo.repo_name,
                username=user.username,
                action='push',
                scm=pull_request.target_repo.repo_type)
            self._merge_pull_request(pull_request, user, extras)

        return redirect(
            url('pullrequest_show',
                repo_name=pull_request.target_repo.repo_name,
                pull_request_id=pull_request.pull_request_id))
    def test_merge_failed(self, pull_request, merge_extras):
        user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
        self.merge_mock.return_value = MergeResponse(
            False, False, '6126b7bfcc82ad2d3deaee22af926b082ce54cc6',
            MergeFailureReason.MERGE_FAILED)

        PullRequestModel().merge(pull_request,
                                 pull_request.author,
                                 extras=merge_extras)

        message = (
            u'Merge pull request #{pr_id} from {source_repo} {source_ref_name}'
            u'\n\n {pr_title}'.format(
                pr_id=pull_request.pull_request_id,
                source_repo=safe_unicode(
                    pull_request.source_repo.scm_instance().name),
                source_ref_name=pull_request.source_ref_parts.name,
                pr_title=safe_unicode(pull_request.title)))
        self.merge_mock.assert_called_once_with(
            pull_request.target_ref_parts,
            pull_request.source_repo.scm_instance(),
            pull_request.source_ref_parts,
            self.workspace_id,
            user_name=user.username,
            user_email=user.email,
            message=message)

        pull_request = PullRequest.get(pull_request.pull_request_id)
        assert self.invalidation_mock.called is False
        assert pull_request.merge_rev is None
    def _prepare_get_all_query(self,
                               repo_name,
                               source=False,
                               statuses=None,
                               opened_by=None,
                               order_by=None,
                               order_dir='desc'):
        repo = self._get_repo(repo_name)
        q = PullRequest.query()
        # source or target
        if source:
            q = q.filter(PullRequest.source_repo == repo)
        else:
            q = q.filter(PullRequest.target_repo == repo)

        # closed,opened
        if statuses:
            q = q.filter(PullRequest.status.in_(statuses))

        # opened by filter
        if opened_by:
            q = q.filter(PullRequest.user_id.in_(opened_by))

        if order_by:
            order_map = {
                'name_raw': PullRequest.pull_request_id,
                'title': PullRequest.title,
                'updated_on_raw': PullRequest.updated_on
            }
            if order_dir == 'asc':
                q = q.order_by(order_map[order_by].asc())
            else:
                q = q.order_by(order_map[order_by].desc())

        return q
예제 #5
0
    def create(self, created_by, org_repo, org_ref, other_repo,
               other_ref, revisions, reviewers, title, description=None):

        created_by_user = self._get_user(created_by)
        org_repo = self._get_repo(org_repo)
        other_repo = self._get_repo(other_repo)

        new = PullRequest()
        new.org_repo = org_repo
        new.org_ref = org_ref
        new.other_repo = other_repo
        new.other_ref = other_ref
        new.revisions = revisions
        new.title = title
        new.description = description
        new.author = created_by_user
        self.sa.add(new)
        Session().flush()
        #members
        for member in reviewers:
            _usr = self._get_user(member)
            reviewer = PullRequestReviewers(_usr, new)
            self.sa.add(reviewer)

        #notification to reviewers
        notif = NotificationModel()

        pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
                       pull_request_id=new.pull_request_id,
                       qualified=True,
        )
        subject = safe_unicode(
            h.link_to(
              _('%(user)s wants you to review pull request #%(pr_id)s') % \
                {'user': created_by_user.username,
                 'pr_id': new.pull_request_id},
                pr_url
            )
        )
        body = description
        kwargs = {
            'pr_title': title,
            'pr_user_created': h.person(created_by_user.email),
            'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
                                 qualified=True,),
            'pr_url': pr_url,
            'pr_revisions': revisions
        }
        notif.create(created_by=created_by_user, subject=subject, body=body,
                     recipients=reviewers,
                     type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
        return new
예제 #6
0
    def show(self, repo_name, pull_request_id):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()
        c.pull_request = PullRequest.get_or_404(pull_request_id)
        c.allowed_to_change_status = self._get_is_allowed_change_status(
            c.pull_request)
        cc_model = ChangesetCommentsModel()
        cs_model = ChangesetStatusModel()
        _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
                                             pull_request=c.pull_request,
                                             with_revisions=True)

        cs_statuses = defaultdict(list)
        for st in _cs_statuses:
            cs_statuses[st.author.username] += [st]

        c.pull_request_reviewers = []
        c.pull_request_pending_reviewers = []
        for o in c.pull_request.reviewers:
            st = cs_statuses.get(o.user.username, None)
            if st:
                sorter = lambda k: k.version
                st = [(x, list(y)[0])
                      for x, y in (groupby(sorted(st, key=sorter), sorter))]
            else:
                c.pull_request_pending_reviewers.append(o.user)
            c.pull_request_reviewers.append([o.user, st])

        # pull_requests repo_name we opened it against
        # ie. other_repo must match
        if repo_name != c.pull_request.other_repo.repo_name:
            raise HTTPNotFound

        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request,
                                enable_comments=enable_comments)

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
            c.rhodecode_db_repo.repo_id, pull_request=pull_request_id)
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)
        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        # (badly named) pull-request status calculation based on reviewer votes
        c.current_changeset_status = cs_model.calculate_status(
            c.pull_request_reviewers, )
        c.changeset_statuses = ChangesetStatus.STATUSES

        c.as_form = False
        c.ancestor = None  # there is one - but right here we don't know which
        return render('/pullrequests/pullrequest_show.html')
예제 #7
0
    def show(self, repo_name, pull_request_id):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()
        c.pull_request = PullRequest.get_or_404(pull_request_id)
        c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
        cc_model = ChangesetCommentsModel()
        cs_model = ChangesetStatusModel()
        _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
                                            pull_request=c.pull_request,
                                            with_revisions=True)

        cs_statuses = defaultdict(list)
        for st in _cs_statuses:
            cs_statuses[st.author.username] += [st]

        c.pull_request_reviewers = []
        c.pull_request_pending_reviewers = []
        for o in c.pull_request.reviewers:
            st = cs_statuses.get(o.user.username, None)
            if st:
                sorter = lambda k: k.version
                st = [(x, list(y)[0])
                      for x, y in (groupby(sorted(st, key=sorter), sorter))]
            else:
                c.pull_request_pending_reviewers.append(o.user)
            c.pull_request_reviewers.append([o.user, st])

        # pull_requests repo_name we opened it against
        # ie. other_repo must match
        if repo_name != c.pull_request.other_repo.repo_name:
            raise HTTPNotFound

        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request, enable_comments=enable_comments)

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
                                c.rhodecode_db_repo.repo_id,
                                pull_request=pull_request_id)
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)
        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        # (badly named) pull-request status calculation based on reviewer votes
        c.current_changeset_status = cs_model.calculate_status(
                                        c.pull_request_reviewers,
                                         )
        c.changeset_statuses = ChangesetStatus.STATUSES

        c.as_form = False
        c.ancestor = None # there is one - but right here we don't know which
        return render('/pullrequests/pullrequest_show.html')
    def create(self,
               created_by,
               source_repo,
               source_ref,
               target_repo,
               target_ref,
               revisions,
               reviewers,
               title,
               description=None):
        created_by_user = self._get_user(created_by)
        source_repo = self._get_repo(source_repo)
        target_repo = self._get_repo(target_repo)

        pull_request = PullRequest()
        pull_request.source_repo = source_repo
        pull_request.source_ref = source_ref
        pull_request.target_repo = target_repo
        pull_request.target_ref = target_ref
        pull_request.revisions = revisions
        pull_request.title = title
        pull_request.description = description
        pull_request.author = created_by_user

        Session().add(pull_request)
        Session().flush()

        # members / reviewers
        for user_id in set(reviewers):
            user = self._get_user(user_id)
            reviewer = PullRequestReviewers(user, pull_request)
            Session().add(reviewer)

        # Set approval status to "Under Review" for all commits which are
        # part of this pull request.
        ChangesetStatusModel().set_status(
            repo=target_repo,
            status=ChangesetStatus.STATUS_UNDER_REVIEW,
            user=created_by_user,
            pull_request=pull_request)

        self.notify_reviewers(pull_request, reviewers)
        self._trigger_pull_request_hook(pull_request, created_by_user,
                                        'create')

        return pull_request
예제 #9
0
    def comment(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        if status and change_status:
            text = text or (_('Status change -> %s')
                            % ChangesetStatus.get_status_lbl(status))
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status else None)
        )

        # get status if set !
        if status and change_status:
            ChangesetStatusModel().set_status(
                c.rhodecode_db_repo.repo_id,
                status,
                c.rhodecode_user.user_id,
                comm,
                pull_request=pull_request_id
            )
        action_logger(self.rhodecode_user,
                      'user_commented_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        if request.POST.get('save_close'):
            PullRequestModel().close_pull_request(pull_request_id)
            action_logger(self.rhodecode_user,
                      'user_closed_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(h.url('pullrequest_show', repo_name=repo_name,
                                  pull_request_id=pull_request_id))

        data = {
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({'rendered_text':
                         render('changeset/changeset_comment_block.html')})

        return data
예제 #10
0
 def delete(self, repo_name, pull_request_id):
     pull_request = PullRequest.get_or_404(pull_request_id)
     #only owner can delete it !
     if pull_request.author.user_id == c.rhodecode_user.user_id:
         PullRequestModel().delete(pull_request)
         Session().commit()
         h.flash(_('Successfully deleted pull request'), category='success')
         return redirect(
             url('admin_settings_my_account', anchor='pullrequests'))
     raise HTTPForbidden()
예제 #11
0
 def delete(self, repo_name, pull_request_id):
     pull_request = PullRequest.get_or_404(pull_request_id)
     #only owner can delete it !
     if pull_request.author.user_id == c.rhodecode_user.user_id:
         PullRequestModel().delete(pull_request)
         Session().commit()
         h.flash(_('Successfully deleted pull request'),
                 category='success')
         return redirect(url('admin_settings_my_account', anchor='pullrequests'))
     raise HTTPForbidden()
    def test_remove_pull_request_branch(self, backend_git, csrf_token):
        branch_name = 'development'
        commits = [
            {'message': 'initial-commit'},
            {'message': 'old-feature'},
            {'message': 'new-feature', 'branch': branch_name},
        ]
        repo = backend_git.create_repo(commits)
        commit_ids = backend_git.commit_ids

        pull_request = PullRequest()
        pull_request.source_repo = repo
        pull_request.target_repo = repo
        pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
            branch=branch_name, commit_id=commit_ids['new-feature'])
        pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend_git.default_branch_name,
            commit_id=commit_ids['old-feature'])
        pull_request.revisions = [commit_ids['new-feature']]
        pull_request.title = u"Test"
        pull_request.description = u"Description"
        pull_request.author = UserModel().get_by_username(
            TEST_USER_ADMIN_LOGIN)
        Session().add(pull_request)
        Session().commit()

        vcs = repo.scm_instance()
        vcs.remove_ref('refs/heads/{}'.format(branch_name))

        response = self.app.get(url(
            controller='pullrequests', action='show',
            repo_name=repo.repo_name,
            pull_request_id=str(pull_request.pull_request_id)))

        assert response.status_int == 200
        assert_response = AssertResponse(response)
        assert_response.element_contains(
            '#changeset_compare_view_content .alert strong',
            'Missing commits')
        assert_response.element_contains(
            '#changeset_compare_view_content .alert',
            'This pull request cannot be displayed, because one or more'
            ' commits no longer exist in the source repository.')
예제 #13
0
 def my_account_my_pullrequests(self):
     c.my_pull_requests = PullRequest.query()\
                             .filter(PullRequest.user_id==
                                     self.rhodecode_user.user_id)\
                             .all()
     c.participate_in_pull_requests = \
         [x.pull_request for x in PullRequestReviewers.query()\
                                 .filter(PullRequestReviewers.user_id==
                                         self.rhodecode_user.user_id)\
                                 .all()]
     return render('admin/users/user_edit_my_account_pullrequests.html')
    def test_reviewer_notifications(self, backend, csrf_token):
        # We have to use the app.post for this test so it will create the
        # notifications properly with the new PR
        commits = [
            {'message': 'ancestor',
             'added': [FileNode('file_A', content='content_of_ancestor')]},
            {'message': 'change',
             'added': [FileNode('file_a', content='content_of_change')]},
            {'message': 'change-child'},
            {'message': 'ancestor-child', 'parents': ['ancestor'],
             'added': [
                FileNode('file_B', content='content_of_ancestor_child')]},
            {'message': 'ancestor-child-2'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor-child'])
        source = backend.create_repo(heads=['change'])

        response = self.app.post(
            url(
                controller='pullrequests',
                action='create',
                repo_name=source.repo_name),
            params={
                'source_repo': source.repo_name,
                'source_ref': 'branch:default:' + commit_ids['change'],
                'target_repo': target.repo_name,
                'target_ref': 'branch:default:' + commit_ids['ancestor-child'],
                'pullrequest_desc': 'Description',
                'pullrequest_title': 'Title',
                'review_members': '2',
                'revisions': commit_ids['change'],
                'user': '',
                'csrf_token': csrf_token,
            },
            status=302)

        location = response.headers['Location']
        pull_request_id = int(location.rsplit('/', 1)[1])
        pull_request = PullRequest.get(pull_request_id)

        # Check that a notification was made
        notifications = Notification.query()\
            .filter(Notification.created_by == pull_request.author.user_id,
                    Notification.type_ == Notification.TYPE_PULL_REQUEST,
                    Notification.subject.contains("wants you to review "
                                                  "pull request #%d"
                                                  % pull_request_id))
        assert len(notifications.all()) == 1

        # Change reviewers and check that a notification was made
        PullRequestModel().update_reviewers(pull_request.pull_request_id, [1])
        assert len(notifications.all()) == 2
예제 #15
0
    def pull_requests(self, pull_request_id):
        """
        Global redirect for Pull Requests

        :param pull_request_id: id of pull requests in the system
        """
        pull_request = PullRequest.get_or_404(pull_request_id)
        repo_name = pull_request.target_repo.repo_name
        return redirect(
            url('pullrequest_show',
                repo_name=repo_name,
                pull_request_id=pull_request_id))
예제 #16
0
 def get_all(self, repo_name, from_=False, closed=False):
     """Get all PRs for repo.
     Default is all PRs to the repo, PRs from the repo if from_.
     Closed PRs are only included if closed is true."""
     repo = self._get_repo(repo_name)
     q = PullRequest.query()
     if from_:
         q = q.filter(PullRequest.org_repo == repo)
     else:
         q = q.filter(PullRequest.other_repo == repo)
     if not closed:
         q = q.filter(PullRequest.status != PullRequest.STATUS_CLOSED)
     return q.order_by(PullRequest.created_on.desc()).all()
예제 #17
0
 def get_all(self, repo_name, from_=False, closed=False):
     """Get all PRs for repo.
     Default is all PRs to the repo, PRs from the repo if from_.
     Closed PRs are only included if closed is true."""
     repo = self._get_repo(repo_name)
     q = PullRequest.query()
     if from_:
         q = q.filter(PullRequest.org_repo == repo)
     else:
         q = q.filter(PullRequest.other_repo == repo)
     if not closed:
         q = q.filter(PullRequest.status != PullRequest.STATUS_CLOSED)
     return q.order_by(PullRequest.created_on.desc()).all()
예제 #18
0
    def update(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()
        #only owner or admin can update it
        owner = pull_request.author.user_id == c.rhodecode_user.user_id
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
            reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
                       request.POST.get('reviewers_ids', '').split(',')))

            PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
            Session().commit()
            return True
        raise HTTPForbidden()
    def test_create_pull_request_stores_ancestor_commit_id(self, backend,
                                                           csrf_token):
        commits = [
            {'message': 'ancestor',
             'added': [FileNode('file_A', content='content_of_ancestor')]},
            {'message': 'change',
             'added': [FileNode('file_a', content='content_of_change')]},
            {'message': 'change-child'},
            {'message': 'ancestor-child', 'parents': ['ancestor'],
             'added': [
                FileNode('file_B', content='content_of_ancestor_child')]},
            {'message': 'ancestor-child-2'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor-child'])
        source = backend.create_repo(heads=['change'])

        response = self.app.post(
            url(
                controller='pullrequests',
                action='create',
                repo_name=source.repo_name),
            params={
                'source_repo': source.repo_name,
                'source_ref': 'branch:default:' + commit_ids['change'],
                'target_repo': target.repo_name,
                'target_ref': 'branch:default:' + commit_ids['ancestor-child'],
                'pullrequest_desc': 'Description',
                'pullrequest_title': 'Title',
                'review_members': '1',
                'revisions': commit_ids['change'],
                'user': '',
                'csrf_token': csrf_token,
            },
            status=302)

        location = response.headers['Location']
        pull_request_id = int(location.rsplit('/', 1)[1])
        pull_request = PullRequest.get(pull_request_id)

        # target_ref has to point to the ancestor's commit_id in order to
        # show the correct diff
        expected_target_ref = 'branch:default:' + commit_ids['ancestor']
        assert pull_request.target_ref == expected_target_ref

        # Check generated diff contents
        response = response.follow()
        assert 'content_of_ancestor' not in response.body
        assert 'content_of_ancestor-child' not in response.body
        assert 'content_of_change' in response.body
    def test_merge_pull_request_disabled(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request(mergeable=False)
        pull_request_id = pull_request.pull_request_id
        pull_request = PullRequest.get(pull_request_id)

        response = self.app.post(
            url(controller='pullrequests',
                action='merge',
                repo_name=pull_request.target_repo.scm_instance().name,
                pull_request_id=str(pull_request.pull_request_id)),
            params={'csrf_token': csrf_token}).follow()

        assert response.status_int == 200
        assert 'Server-side pull request merging is disabled.' in response.body
예제 #21
0
    def update(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()
        #only owner or admin can update it
        owner = pull_request.author.user_id == c.rhodecode_user.user_id
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
            reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
                       request.POST.get('reviewers_ids', '').split(',')))

            PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
            Session().commit()
            return True
        raise HTTPForbidden()
    def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request(mergeable=True)
        pull_request_id = pull_request.pull_request_id
        repo_name = pull_request.target_repo.scm_instance().name,

        response = self.app.post(
            url(controller='pullrequests',
                action='merge',
                repo_name=str(repo_name[0]),
                pull_request_id=str(pull_request_id)),
            params={'csrf_token': csrf_token}).follow()

        pull_request = PullRequest.get(pull_request_id)

        assert response.status_int == 200
        assert ' Reviewer approval is pending.' in response.body
예제 #23
0
 def update(self, repo_name, pull_request_id):
     pull_request_id = safe_int(pull_request_id)
     pull_request = PullRequest.get_or_404(pull_request_id)
     # only owner or admin can update it
     allowed_to_update = PullRequestModel().check_user_update(
         pull_request, c.rhodecode_user)
     if allowed_to_update:
         if 'reviewers_ids' in request.POST:
             self._update_reviewers(pull_request_id)
         elif str2bool(request.POST.get('update_commits', 'false')):
             self._update_commits(pull_request)
         elif str2bool(request.POST.get('close_pull_request', 'false')):
             self._reject_close(pull_request)
         elif str2bool(request.POST.get('edit_pull_request', 'false')):
             self._edit_pull_request(pull_request)
         else:
             raise HTTPBadRequest()
         return True
     raise HTTPForbidden()
    def test_merge_pull_request_enabled(self, pr_util, csrf_token):
        # Clear any previous calls to rcextensions
        rhodecode.EXTENSIONS.calls.clear()

        pull_request = pr_util.create_pull_request(
            approved=True, mergeable=True)
        pull_request_id = pull_request.pull_request_id
        repo_name = pull_request.target_repo.scm_instance().name,

        response = self.app.post(
            url(controller='pullrequests',
                action='merge',
                repo_name=str(repo_name[0]),
                pull_request_id=str(pull_request_id)),
            params={'csrf_token': csrf_token}).follow()

        pull_request = PullRequest.get(pull_request_id)

        assert response.status_int == 200
        assert pull_request.is_closed()
        assert_pull_request_status(
            pull_request, ChangesetStatus.STATUS_APPROVED)

        # Check the relevant log entries were added
        user_logs = UserLog.query().order_by('-user_log_id').limit(4)
        actions = [log.action for log in user_logs]
        pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
        expected_actions = [
            u'user_closed_pull_request:%d' % pull_request_id,
            u'user_merged_pull_request:%d' % pull_request_id,
            # The action below reflect that the post push actions were executed
            u'user_commented_pull_request:%d' % pull_request_id,
            u'push:%s' % ','.join(pr_commit_ids),
        ]
        assert actions == expected_actions

        # Check post_push rcextension was really executed
        push_calls = rhodecode.EXTENSIONS.calls['post_push']
        assert len(push_calls) == 1
        unused_last_call_args, last_call_kwargs = push_calls[0]
        assert last_call_kwargs['action'] == 'push'
        assert last_call_kwargs['pushed_revs'] == pr_commit_ids
예제 #25
0
    def my_account_my_pullrequests(self):
        c.show_closed = request.GET.get('pr_show_closed')

        def _filter(pr):
            s = sorted(pr, key=lambda o: o.created_on, reverse=True)
            if not c.show_closed:
                s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
            return s

        c.my_pull_requests = _filter(PullRequest.query()\
                                .filter(PullRequest.user_id ==
                                        self.rhodecode_user.user_id)\
                                .all())

        c.participate_in_pull_requests = _filter([
                    x.pull_request for x in PullRequestReviewers.query()\
                    .filter(PullRequestReviewers.user_id ==
                            self.rhodecode_user.user_id).all()])

        return render('admin/users/user_edit_my_account_pullrequests.html')
    def test_reject_and_close_pull_request(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id
        response = self.app.post(
            url(controller='pullrequests',
                action='update',
                repo_name=pull_request.target_repo.scm_instance().name,
                pull_request_id=str(pull_request.pull_request_id)),
            params={'close_pull_request': 'true', '_method': 'put',
                    'csrf_token': csrf_token})

        pull_request = PullRequest.get(pull_request_id)

        assert response.json is True
        assert pull_request.is_closed()

        # check only the latest status, not the review status
        status = ChangesetStatusModel().get_status(
            pull_request.source_repo, pull_request=pull_request)
        assert status == ChangesetStatus.STATUS_REJECTED
예제 #27
0
    def my_account_my_pullrequests(self):
        c.show_closed = request.GET.get('pr_show_closed')

        def _filter(pr):
            s = sorted(pr, key=lambda o: o.created_on, reverse=True)
            if not c.show_closed:
                s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
            return s

        c.my_pull_requests = _filter(PullRequest.query()\
                                .filter(PullRequest.user_id ==
                                        self.rhodecode_user.user_id)\
                                .all())

        c.participate_in_pull_requests = _filter([
                    x.pull_request for x in PullRequestReviewers.query()\
                    .filter(PullRequestReviewers.user_id ==
                            self.rhodecode_user.user_id).all()])

        return render('admin/users/user_edit_my_account_pullrequests.html')
예제 #28
0
    def my_account_pullrequests(self):
        c.active = 'pullrequests'
        self.__load_data()
        c.show_closed = request.GET.get('pr_show_closed')

        def _filter(pr):
            s = sorted(pr, key=lambda o: o.created_on, reverse=True)
            if not c.show_closed:
                s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
            return s

        c.my_pull_requests = _filter(PullRequest.query().filter(
            PullRequest.user_id == c.rhodecode_user.user_id).all())
        my_prs = [
            x.pull_request for x in PullRequestReviewers.query().filter(
                PullRequestReviewers.user_id ==
                c.rhodecode_user.user_id).all()
        ]
        c.participate_in_pull_requests = _filter(my_prs)
        return render('admin/my_account/my_account.html')
    def test_edit_title_description(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id

        response = self.app.post(
            url(controller='pullrequests', action='update',
                repo_name=pull_request.target_repo.repo_name,
                pull_request_id=str(pull_request_id)),
            params={
                'edit_pull_request': 'true',
                '_method': 'put',
                'title': 'New title',
                'description': 'New description',
                'csrf_token': csrf_token})

        assert_session_flash(
            response, u'Pull request title & description updated.',
            category='success')

        pull_request = PullRequest.get(pull_request_id)
        assert pull_request.title == 'New title'
        assert pull_request.description == 'New description'
    def test_create_pull_request(self, backend, csrf_token):
        commits = [
            {'message': 'ancestor'},
            {'message': 'change'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor'])
        source = backend.create_repo(heads=['change'])

        response = self.app.post(
            url(
                controller='pullrequests',
                action='create',
                repo_name=source.repo_name),
            params={
                'source_repo': source.repo_name,
                'source_ref': 'branch:default:' + commit_ids['change'],
                'target_repo': target.repo_name,
                'target_ref': 'branch:default:' + commit_ids['ancestor'],
                'pullrequest_desc': 'Description',
                'pullrequest_title': 'Title',
                'review_members': '1',
                'revisions': commit_ids['change'],
                'user': '',
                'csrf_token': csrf_token,
            },
            status=302)

        location = response.headers['Location']
        pull_request_id = int(location.rsplit('/', 1)[1])
        pull_request = PullRequest.get(pull_request_id)

        # check that we have now both revisions
        assert pull_request.revisions == [commit_ids['change']]
        assert pull_request.source_ref == 'branch:default:' + commit_ids['change']
        expected_target_ref = 'branch:default:' + commit_ids['ancestor']
        assert pull_request.target_ref == expected_target_ref
예제 #31
0
    def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
               revisions, reviewers, title, description=None):
        from rhodecode.model.changeset_status import ChangesetStatusModel

        created_by_user = self._get_user(created_by)
        org_repo = self._get_repo(org_repo)
        other_repo = self._get_repo(other_repo)

        new = PullRequest()
        new.org_repo = org_repo
        new.org_ref = org_ref
        new.other_repo = other_repo
        new.other_ref = other_ref
        new.revisions = revisions
        new.title = title
        new.description = description
        new.author = created_by_user
        Session().add(new)
        Session().flush()
        #members
        for member in set(reviewers):
            _usr = self._get_user(member)
            reviewer = PullRequestReviewers(_usr, new)
            Session().add(reviewer)

        #reset state to under-review
        ChangesetStatusModel().set_status(
            repo=org_repo,
            status=ChangesetStatus.STATUS_UNDER_REVIEW,
            user=created_by_user,
            pull_request=new
        )
        revision_data = [(x.raw_id, x.message)
                         for x in map(org_repo.get_changeset, revisions)]
        #notification to reviewers
        pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
                       pull_request_id=new.pull_request_id,
                       qualified=True,
        )
        subject = safe_unicode(
            h.link_to(
              _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
                {'user': created_by_user.username,
                 'pr_title': new.title,
                 'pr_id': new.pull_request_id},
                pr_url
            )
        )
        body = description
        kwargs = {
            'pr_title': title,
            'pr_user_created': h.person(created_by_user.email),
            'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
                                 qualified=True,),
            'pr_url': pr_url,
            'pr_revisions': revision_data
        }

        NotificationModel().create(created_by=created_by_user, subject=subject, body=body,
                                   recipients=reviewers,
                                   type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
        return new
    def test_update_of_ancestor_reference(self, backend, csrf_token):
        commits = [
            {'message': 'ancestor'},
            {'message': 'change'},
            {'message': 'change-2'},
            {'message': 'ancestor-new', 'parents': ['ancestor']},
            {'message': 'change-rebased'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor'])
        source = backend.create_repo(heads=['change'])

        # create pr from a in source to A in target
        pull_request = PullRequest()
        pull_request.source_repo = source
        # TODO: johbo: Make sure that we write the source ref this way!
        pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name,
            commit_id=commit_ids['change'])
        pull_request.target_repo = target
        # TODO: johbo: Target ref should be branch based, since tip can jump
        # from branch to branch
        pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name,
            commit_id=commit_ids['ancestor'])
        pull_request.revisions = [commit_ids['change']]
        pull_request.title = u"Test"
        pull_request.description = u"Description"
        pull_request.author = UserModel().get_by_username(
            TEST_USER_ADMIN_LOGIN)
        Session().add(pull_request)
        Session().commit()
        pull_request_id = pull_request.pull_request_id

        # target has ancestor - ancestor-new
        # source has ancestor - ancestor-new - change-rebased
        backend.pull_heads(target, heads=['ancestor-new'])
        backend.pull_heads(source, heads=['change-rebased'])

        # update PR
        self.app.post(
            url(controller='pullrequests', action='update',
                repo_name=target.repo_name,
                pull_request_id=str(pull_request_id)),
            params={'update_commits': 'true', '_method': 'put',
                    'csrf_token': csrf_token},
            status=200)

        # Expect the target reference to be updated correctly
        pull_request = PullRequest.get(pull_request_id)
        assert pull_request.revisions == [commit_ids['change-rebased']]
        expected_target_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name,
            commit_id=commit_ids['ancestor-new'])
        assert pull_request.target_ref == expected_target_ref
예제 #33
0
 def get_all(self, repo):
     repo = self._get_repo(repo)
     return PullRequest.query().filter(PullRequest.other_repo == repo).all()
예제 #34
0
    def show(self, repo_name, pull_request_id):
        pull_request_id = safe_int(pull_request_id)
        c.pull_request = PullRequest.get_or_404(pull_request_id)

        # pull_requests repo_name we opened it against
        # ie. target_repo must match
        if repo_name != c.pull_request.target_repo.repo_name:
            raise HTTPNotFound

        c.allowed_to_change_status = PullRequestModel(). \
            check_user_change_status(c.pull_request, c.rhodecode_user)
        c.allowed_to_update = PullRequestModel().check_user_update(
            c.pull_request,
            c.rhodecode_user) and not c.pull_request.is_closed()
        c.allowed_to_merge = PullRequestModel().check_user_merge(
            c.pull_request,
            c.rhodecode_user) and not c.pull_request.is_closed()

        cc_model = ChangesetCommentsModel()

        c.pull_request_reviewers = c.pull_request.reviewers_statuses()

        c.pull_request_review_status = c.pull_request.calculated_review_status(
        )
        c.pr_merge_status, c.pr_merge_msg = PullRequestModel().merge_status(
            c.pull_request)
        c.approval_msg = None
        if c.pull_request_review_status != ChangesetStatus.STATUS_APPROVED:
            c.approval_msg = _('Reviewer approval is pending.')
            c.pr_merge_status = False
        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request,
                                enable_comments=enable_comments)

        # this is a hack to properly display links, when creating PR, the
        # compare view and others uses different notation, and
        # compare_commits.html renders links based on the target_repo.
        # We need to swap that here to generate it properly on the html side
        c.target_repo = c.source_repo

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
            c.rhodecode_db_repo.repo_id, pull_request=pull_request_id).items()
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        # outdated comments
        c.outdated_cnt = 0
        if ChangesetCommentsModel.use_outdated_comments(c.pull_request):
            c.outdated_comments = cc_model.get_outdated_comments(
                c.rhodecode_db_repo.repo_id, pull_request=c.pull_request)
            # Count outdated comments and check for deleted files
            for file_name, lines in c.outdated_comments.iteritems():
                for comments in lines.values():
                    c.outdated_cnt += len(comments)
                if file_name not in c.included_files:
                    c.deleted_files.append(file_name)
        else:
            c.outdated_comments = {}

        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        if c.allowed_to_update:
            force_close = ('forced_closed', _('Close Pull Request'))
            statuses = ChangesetStatus.STATUSES + [force_close]
        else:
            statuses = ChangesetStatus.STATUSES
        c.commit_statuses = statuses

        c.ancestor = None  # TODO: add ancestor here

        return render('/pullrequests/pullrequest_show.html')
예제 #35
0
    def comment(self, repo_name, pull_request_id):
        pull_request_id = safe_int(pull_request_id)
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        # TODO: johbo: Re-think this bit, "approved_closed" does not exist
        # as a changeset status, still we want to send it in one value.
        status = request.POST.get('changeset_status', None)
        text = request.POST.get('text')
        if status and '_closed' in status:
            close_pr = True
            status = status.replace('_closed', '')
        else:
            close_pr = False

        forced = (status == 'forced')
        if forced:
            status = 'rejected'

        allowed_to_change_status = PullRequestModel().check_user_change_status(
            pull_request, c.rhodecode_user)

        if status and allowed_to_change_status:
            message = (_('Status change %(transition_icon)s %(status)s') % {
                'transition_icon': '>',
                'status': ChangesetStatus.get_status_lbl(status)
            })
            if close_pr:
                message = _('Closing with') + ' ' + message
            text = text or message
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and allowed_to_change_status else None),
            closing_pr=close_pr)

        if allowed_to_change_status:
            old_calculated_status = pull_request.calculated_review_status()
            # get status if set !
            if status:
                ChangesetStatusModel().set_status(c.rhodecode_db_repo.repo_id,
                                                  status,
                                                  c.rhodecode_user.user_id,
                                                  comm,
                                                  pull_request=pull_request_id)

            Session().flush()
            # we now calculate the status of pull request, and based on that
            # calculation we set the commits status
            calculated_status = pull_request.calculated_review_status()
            if old_calculated_status != calculated_status:
                PullRequestModel()._trigger_pull_request_hook(
                    pull_request, c.rhodecode_user, 'review_status_change')

            calculated_status_lbl = ChangesetStatus.get_status_lbl(
                calculated_status)

            if close_pr:
                status_completed = (calculated_status in [
                    ChangesetStatus.STATUS_APPROVED,
                    ChangesetStatus.STATUS_REJECTED
                ])
                if forced or status_completed:
                    PullRequestModel().close_pull_request(
                        pull_request_id, c.rhodecode_user)
                else:
                    h.flash(_('Closing pull request on other statuses than '
                              'rejected or approved is forbidden. '
                              'Calculated status from all reviewers '
                              'is currently: %s') % calculated_status_lbl,
                            category='warning')

        Session().commit()

        if not request.is_xhr:
            return redirect(
                h.url('pullrequest_show',
                      repo_name=repo_name,
                      pull_request_id=pull_request_id))

        data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({
                'rendered_text':
                render('changeset/changeset_comment_block.html')
            })

        return data
예제 #36
0
    def show(self, repo_name, pull_request_id):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()
        c.pull_request = PullRequest.get_or_404(pull_request_id)
        c.target_repo = c.pull_request.org_repo.repo_name

        cc_model = ChangesetCommentsModel()
        cs_model = ChangesetStatusModel()
        _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
                                            pull_request=c.pull_request,
                                            with_revisions=True)

        cs_statuses = defaultdict(list)
        for st in _cs_statuses:
            cs_statuses[st.author.username] += [st]

        c.pull_request_reviewers = []
        c.pull_request_pending_reviewers = []
        for o in c.pull_request.reviewers:
            st = cs_statuses.get(o.user.username, None)
            if st:
                sorter = lambda k: k.version
                st = [(x, list(y)[0])
                      for x, y in (groupby(sorted(st, key=sorter), sorter))]
            else:
                c.pull_request_pending_reviewers.append(o.user)
            c.pull_request_reviewers.append([o.user, st])

        # pull_requests repo_name we opened it against
        # ie. other_repo must match
        if repo_name != c.pull_request.other_repo.repo_name:
            raise HTTPNotFound

        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request, enable_comments=enable_comments)

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
                                c.rhodecode_db_repo.repo_id,
                                pull_request=pull_request_id)
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)
        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        try:
            cur_status = c.statuses[c.pull_request.revisions[0]][0]
        except:
            log.error(traceback.format_exc())
            cur_status = 'undefined'
        if c.pull_request.is_closed() and 0:
            c.current_changeset_status = cur_status
        else:
            # changeset(pull-request) status calulation based on reviewers
            c.current_changeset_status = cs_model.calculate_status(
                                            c.pull_request_reviewers,
                                         )
        c.changeset_statuses = ChangesetStatus.STATUSES

        return render('/pullrequests/pullrequest_show.html')
예제 #37
0
    def comment(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        close_pr = request.POST.get('save_close')

        allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
        if status and change_status and allowed_to_change_status:
            _def = (_('Status change -> %s')
                            % ChangesetStatus.get_status_lbl(status))
            if close_pr:
                _def = _('Closing with') + ' ' + _def
            text = text or _def
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status
                           and allowed_to_change_status else None),
            closing_pr=close_pr
        )

        action_logger(self.rhodecode_user,
                      'user_commented_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        if allowed_to_change_status:
            # get status if set !
            if status and change_status:
                ChangesetStatusModel().set_status(
                    c.rhodecode_db_repo.repo_id,
                    status,
                    c.rhodecode_user.user_id,
                    comm,
                    pull_request=pull_request_id
                )

            if close_pr:
                if status in ['rejected', 'approved']:
                    PullRequestModel().close_pull_request(pull_request_id)
                    action_logger(self.rhodecode_user,
                              'user_closed_pull_request:%s' % pull_request_id,
                              c.rhodecode_db_repo, self.ip_addr, self.sa)
                else:
                    h.flash(_('Closing pull request on other statuses than '
                              'rejected or approved forbidden'),
                            category='warning')

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(h.url('pullrequest_show', repo_name=repo_name,
                                  pull_request_id=pull_request_id))

        data = {
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({'rendered_text':
                         render('changeset/changeset_comment_block.html')})

        return data
예제 #38
0
    def comment(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        close_pr = request.POST.get('save_close')

        allowed_to_change_status = self._get_is_allowed_change_status(
            pull_request)
        if status and change_status and allowed_to_change_status:
            _def = (_('Status change -> %s') %
                    ChangesetStatus.get_status_lbl(status))
            if close_pr:
                _def = _('Closing with') + ' ' + _def
            text = text or _def
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status
                           and allowed_to_change_status else None),
            closing_pr=close_pr)

        action_logger(self.rhodecode_user,
                      'user_commented_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        if allowed_to_change_status:
            # get status if set !
            if status and change_status:
                ChangesetStatusModel().set_status(c.rhodecode_db_repo.repo_id,
                                                  status,
                                                  c.rhodecode_user.user_id,
                                                  comm,
                                                  pull_request=pull_request_id)

            if close_pr:
                if status in ['rejected', 'approved']:
                    PullRequestModel().close_pull_request(pull_request_id)
                    action_logger(
                        self.rhodecode_user,
                        'user_closed_pull_request:%s' % pull_request_id,
                        c.rhodecode_db_repo, self.ip_addr, self.sa)
                else:
                    h.flash(_('Closing pull request on other statuses than '
                              'rejected or approved forbidden'),
                            category='warning')

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(
                h.url('pullrequest_show',
                      repo_name=repo_name,
                      pull_request_id=pull_request_id))

        data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({
                'rendered_text':
                render('changeset/changeset_comment_block.html')
            })

        return data
    def test_update_source_revision(self, backend, csrf_token):
        commits = [
            {'message': 'ancestor'},
            {'message': 'change'},
            {'message': 'change-2'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor'])
        source = backend.create_repo(heads=['change'])

        # create pr from a in source to A in target
        pull_request = PullRequest()
        pull_request.source_repo = source
        # TODO: johbo: Make sure that we write the source ref this way!
        pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name, commit_id=commit_ids['change'])
        pull_request.target_repo = target

        pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name,
            commit_id=commit_ids['ancestor'])
        pull_request.revisions = [commit_ids['change']]
        pull_request.title = u"Test"
        pull_request.description = u"Description"
        pull_request.author = UserModel().get_by_username(
            TEST_USER_ADMIN_LOGIN)
        Session().add(pull_request)
        Session().commit()
        pull_request_id = pull_request.pull_request_id

        # source has ancestor - change - change-2
        backend.pull_heads(source, heads=['change-2'])

        # update PR
        self.app.post(
            url(controller='pullrequests', action='update',
                repo_name=target.repo_name,
                pull_request_id=str(pull_request_id)),
            params={'update_commits': 'true', '_method': 'put',
                    'csrf_token': csrf_token})

        # check that we have now both revisions
        pull_request = PullRequest.get(pull_request_id)
        assert pull_request.revisions == [
            commit_ids['change-2'], commit_ids['change']]

        # TODO: johbo: this should be a test on its own
        response = self.app.get(url(
            controller='pullrequests', action='index',
            repo_name=target.repo_name))
        assert response.status_int == 200
        assert 'Pull request updated to' in response.body
        assert 'with 1 added, 0 removed commits.' in response.body
예제 #40
0
    def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
               revisions, reviewers, title, description=None):
        from rhodecode.model.changeset_status import ChangesetStatusModel

        created_by_user = self._get_user(created_by)
        org_repo = self._get_repo(org_repo)
        other_repo = self._get_repo(other_repo)

        new = PullRequest()
        new.org_repo = org_repo
        new.org_ref = org_ref
        new.other_repo = other_repo
        new.other_ref = other_ref
        new.revisions = revisions
        new.title = title
        new.description = description
        new.author = created_by_user
        Session().add(new)
        Session().flush()
        #members
        for member in set(reviewers):
            _usr = self._get_user(member)
            reviewer = PullRequestReviewers(_usr, new)
            Session().add(reviewer)

        #reset state to under-review
        ChangesetStatusModel().set_status(
            repo=org_repo,
            status=ChangesetStatus.STATUS_UNDER_REVIEW,
            user=created_by_user,
            pull_request=new
        )
        revision_data = [(x.raw_id, x.message)
                         for x in map(org_repo.get_changeset, revisions)]
        #notification to reviewers
        notif = NotificationModel()

        pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
                       pull_request_id=new.pull_request_id,
                       qualified=True,
        )
        subject = safe_unicode(
            h.link_to(
              _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
                {'user': created_by_user.username,
                 'pr_title': new.title,
                 'pr_id': new.pull_request_id},
                pr_url
            )
        )
        body = description
        kwargs = {
            'pr_title': title,
            'pr_user_created': h.person(created_by_user.email),
            'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
                                 qualified=True,),
            'pr_url': pr_url,
            'pr_revisions': revision_data
        }

        notif.create(created_by=created_by_user, subject=subject, body=body,
                     recipients=reviewers,
                     type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
        return new
예제 #41
0
 def get_all(self, repo):
     repo = self._get_repo(repo)
     return PullRequest.query()\
             .filter(PullRequest.other_repo == repo)\
             .order_by(PullRequest.created_on.desc())\
             .all()