Show More
@@ -95,9 +95,10 b' class TestGetPullRequest(object):' | |||||
95 | { |
|
95 | { | |
96 | 'user': reviewer.get_api_data(include_secrets=False, |
|
96 | 'user': reviewer.get_api_data(include_secrets=False, | |
97 | details='basic'), |
|
97 | details='basic'), | |
|
98 | 'reasons': reasons, | |||
98 | 'review_status': st[0][1].status if st else 'not_reviewed', |
|
99 | 'review_status': st[0][1].status if st else 'not_reviewed', | |
99 | } |
|
100 | } | |
100 | for reviewer, st in pull_request.reviewers_statuses() |
|
101 | for reviewer, reasons, st in pull_request.reviewers_statuses() | |
101 | ] |
|
102 | ] | |
102 | } |
|
103 | } | |
103 | assert_ok(id_, expected, response.body) |
|
104 | assert_ok(id_, expected, response.body) |
@@ -463,7 +463,12 b' def create_pull_request(' | |||||
463 | :type description: Optional(str) |
|
463 | :type description: Optional(str) | |
464 | :param reviewers: Set the new pull request reviewers list. |
|
464 | :param reviewers: Set the new pull request reviewers list. | |
465 | :type reviewers: Optional(list) |
|
465 | :type reviewers: Optional(list) | |
|
466 | Accepts username strings or objects of the format: | |||
|
467 | { | |||
|
468 | 'username': 'nick', 'reasons': ['original author'] | |||
|
469 | } | |||
466 | """ |
|
470 | """ | |
|
471 | ||||
467 | source = get_repo_or_error(source_repo) |
|
472 | source = get_repo_or_error(source_repo) | |
468 | target = get_repo_or_error(target_repo) |
|
473 | target = get_repo_or_error(target_repo) | |
469 | if not has_superadmin_permission(apiuser): |
|
474 | if not has_superadmin_permission(apiuser): | |
@@ -490,12 +495,21 b' def create_pull_request(' | |||||
490 | if not ancestor: |
|
495 | if not ancestor: | |
491 | raise JSONRPCError('no common ancestor found') |
|
496 | raise JSONRPCError('no common ancestor found') | |
492 |
|
497 | |||
493 |
reviewer_ |
|
498 | reviewer_objects = Optional.extract(reviewers) or [] | |
494 |
if not isinstance(reviewer_ |
|
499 | if not isinstance(reviewer_objects, list): | |
495 | raise JSONRPCError('reviewers should be specified as a list') |
|
500 | raise JSONRPCError('reviewers should be specified as a list') | |
496 |
|
501 | |||
497 | reviewer_users = [get_user_or_error(n) for n in reviewer_names] |
|
502 | reviewers_reasons = [] | |
498 | reviewer_ids = [u.user_id for u in reviewer_users] |
|
503 | for reviewer_object in reviewer_objects: | |
|
504 | reviewer_reasons = [] | |||
|
505 | if isinstance(reviewer_object, (basestring, int)): | |||
|
506 | reviewer_username = reviewer_object | |||
|
507 | else: | |||
|
508 | reviewer_username = reviewer_object['username'] | |||
|
509 | reviewer_reasons = reviewer_object.get('reasons', []) | |||
|
510 | ||||
|
511 | user = get_user_or_error(reviewer_username) | |||
|
512 | reviewers_reasons.append((user.user_id, reviewer_reasons)) | |||
499 |
|
513 | |||
500 | pull_request_model = PullRequestModel() |
|
514 | pull_request_model = PullRequestModel() | |
501 | pull_request = pull_request_model.create( |
|
515 | pull_request = pull_request_model.create( | |
@@ -506,7 +520,7 b' def create_pull_request(' | |||||
506 | target_ref=full_target_ref, |
|
520 | target_ref=full_target_ref, | |
507 | revisions=reversed( |
|
521 | revisions=reversed( | |
508 | [commit.raw_id for commit in reversed(commit_ranges)]), |
|
522 | [commit.raw_id for commit in reversed(commit_ranges)]), | |
509 |
reviewers=reviewer |
|
523 | reviewers=reviewers_reasons, | |
510 | title=title, |
|
524 | title=title, | |
511 | description=Optional.extract(description) |
|
525 | description=Optional.extract(description) | |
512 | ) |
|
526 | ) | |
@@ -585,12 +599,23 b' def update_pull_request(' | |||||
585 | 'pull request `%s` update failed, pull request is closed' % ( |
|
599 | 'pull request `%s` update failed, pull request is closed' % ( | |
586 | pullrequestid,)) |
|
600 | pullrequestid,)) | |
587 |
|
601 | |||
588 |
reviewer_ |
|
602 | reviewer_objects = Optional.extract(reviewers) or [] | |
589 |
if not isinstance(reviewer_ |
|
603 | if not isinstance(reviewer_objects, list): | |
590 | raise JSONRPCError('reviewers should be specified as a list') |
|
604 | raise JSONRPCError('reviewers should be specified as a list') | |
591 |
|
605 | |||
592 | reviewer_users = [get_user_or_error(n) for n in reviewer_names] |
|
606 | reviewers_reasons = [] | |
593 | reviewer_ids = [u.user_id for u in reviewer_users] |
|
607 | reviewer_ids = set() | |
|
608 | for reviewer_object in reviewer_objects: | |||
|
609 | reviewer_reasons = [] | |||
|
610 | if isinstance(reviewer_object, (int, basestring)): | |||
|
611 | reviewer_username = reviewer_object | |||
|
612 | else: | |||
|
613 | reviewer_username = reviewer_object['username'] | |||
|
614 | reviewer_reasons = reviewer_object.get('reasons', []) | |||
|
615 | ||||
|
616 | user = get_user_or_error(reviewer_username) | |||
|
617 | reviewer_ids.add(user.user_id) | |||
|
618 | reviewers_reasons.append((user.user_id, reviewer_reasons)) | |||
594 |
|
619 | |||
595 | title = Optional.extract(title) |
|
620 | title = Optional.extract(title) | |
596 | description = Optional.extract(description) |
|
621 | description = Optional.extract(description) | |
@@ -611,7 +636,7 b' def update_pull_request(' | |||||
611 | reviewers_changes = {"added": [], "removed": []} |
|
636 | reviewers_changes = {"added": [], "removed": []} | |
612 | if reviewer_ids: |
|
637 | if reviewer_ids: | |
613 | added_reviewers, removed_reviewers = \ |
|
638 | added_reviewers, removed_reviewers = \ | |
614 |
PullRequestModel().update_reviewers(pull_request, reviewer |
|
639 | PullRequestModel().update_reviewers(pull_request, reviewers_reasons) | |
615 |
|
640 | |||
616 | reviewers_changes['added'] = sorted( |
|
641 | reviewers_changes['added'] = sorted( | |
617 | [get_user_or_error(n).username for n in added_reviewers]) |
|
642 | [get_user_or_error(n).username for n in added_reviewers]) |
@@ -22,6 +22,7 b'' | |||||
22 | pull requests controller for rhodecode for initializing pull requests |
|
22 | pull requests controller for rhodecode for initializing pull requests | |
23 | """ |
|
23 | """ | |
24 |
|
24 | |||
|
25 | import peppercorn | |||
25 | import formencode |
|
26 | import formencode | |
26 | import logging |
|
27 | import logging | |
27 |
|
28 | |||
@@ -399,8 +400,10 b' class PullrequestsController(BaseRepoCon' | |||||
399 | if not repo: |
|
400 | if not repo: | |
400 | raise HTTPNotFound |
|
401 | raise HTTPNotFound | |
401 |
|
402 | |||
|
403 | controls = peppercorn.parse(request.POST.items()) | |||
|
404 | ||||
402 | try: |
|
405 | try: | |
403 |
_form = PullRequestForm(repo.repo_id)().to_python( |
|
406 | _form = PullRequestForm(repo.repo_id)().to_python(controls) | |
404 | except formencode.Invalid as errors: |
|
407 | except formencode.Invalid as errors: | |
405 | if errors.error_dict.get('revisions'): |
|
408 | if errors.error_dict.get('revisions'): | |
406 | msg = 'Revisions: %s' % errors.error_dict['revisions'] |
|
409 | msg = 'Revisions: %s' % errors.error_dict['revisions'] | |
@@ -419,7 +422,8 b' class PullrequestsController(BaseRepoCon' | |||||
419 | target_repo = _form['target_repo'] |
|
422 | target_repo = _form['target_repo'] | |
420 | target_ref = _form['target_ref'] |
|
423 | target_ref = _form['target_ref'] | |
421 | commit_ids = _form['revisions'][::-1] |
|
424 | commit_ids = _form['revisions'][::-1] | |
422 |
reviewers = |
|
425 | reviewers = [ | |
|
426 | (r['user_id'], r['reasons']) for r in _form['review_members']] | |||
423 |
|
427 | |||
424 | # find the ancestor for this pr |
|
428 | # find the ancestor for this pr | |
425 | source_db_repo = Repository.get_by_repo_name(_form['source_repo']) |
|
429 | source_db_repo = Repository.get_by_repo_name(_form['source_repo']) | |
@@ -478,8 +482,11 b' class PullrequestsController(BaseRepoCon' | |||||
478 | allowed_to_update = PullRequestModel().check_user_update( |
|
482 | allowed_to_update = PullRequestModel().check_user_update( | |
479 | pull_request, c.rhodecode_user) |
|
483 | pull_request, c.rhodecode_user) | |
480 | if allowed_to_update: |
|
484 | if allowed_to_update: | |
481 | if 'reviewers_ids' in request.POST: |
|
485 | controls = peppercorn.parse(request.POST.items()) | |
482 | self._update_reviewers(pull_request_id) |
|
486 | ||
|
487 | if 'review_members' in controls: | |||
|
488 | self._update_reviewers( | |||
|
489 | pull_request_id, controls['review_members']) | |||
483 | elif str2bool(request.POST.get('update_commits', 'false')): |
|
490 | elif str2bool(request.POST.get('update_commits', 'false')): | |
484 | self._update_commits(pull_request) |
|
491 | self._update_commits(pull_request) | |
485 | elif str2bool(request.POST.get('close_pull_request', 'false')): |
|
492 | elif str2bool(request.POST.get('close_pull_request', 'false')): | |
@@ -631,11 +638,10 b' class PullrequestsController(BaseRepoCon' | |||||
631 | merge_resp.failure_reason) |
|
638 | merge_resp.failure_reason) | |
632 | h.flash(msg, category='error') |
|
639 | h.flash(msg, category='error') | |
633 |
|
640 | |||
634 | def _update_reviewers(self, pull_request_id): |
|
641 | def _update_reviewers(self, pull_request_id, review_members): | |
635 |
reviewers |
|
642 | reviewers = [ | |
636 | lambda v: v not in [None, ''], |
|
643 | (int(r['user_id']), r['reasons']) for r in review_members] | |
637 | request.POST.get('reviewers_ids', '').split(','))) |
|
644 | PullRequestModel().update_reviewers(pull_request_id, reviewers) | |
638 | PullRequestModel().update_reviewers(pull_request_id, reviewers_ids) |
|
|||
639 | Session().commit() |
|
645 | Session().commit() | |
640 |
|
646 | |||
641 | def _reject_close(self, pull_request): |
|
647 | def _reject_close(self, pull_request): |
@@ -59,7 +59,7 b' from rhodecode.lib.utils2 import (' | |||||
59 | str2bool, safe_str, get_commit_safe, safe_unicode, remove_prefix, md5_safe, |
|
59 | str2bool, safe_str, get_commit_safe, safe_unicode, remove_prefix, md5_safe, | |
60 | time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict, |
|
60 | time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict, | |
61 | glob2re) |
|
61 | glob2re) | |
62 | from rhodecode.lib.jsonalchemy import MutationObj, JsonType, JSONDict |
|
62 | from rhodecode.lib.jsonalchemy import MutationObj, MutationList, JsonType, JSONDict | |
63 | from rhodecode.lib.ext_json import json |
|
63 | from rhodecode.lib.ext_json import json | |
64 | from rhodecode.lib.caching_query import FromCache |
|
64 | from rhodecode.lib.caching_query import FromCache | |
65 | from rhodecode.lib.encrypt import AESCipher |
|
65 | from rhodecode.lib.encrypt import AESCipher | |
@@ -3149,9 +3149,10 b' class PullRequest(Base, _PullRequestBase' | |||||
3149 | { |
|
3149 | { | |
3150 | 'user': reviewer.get_api_data(include_secrets=False, |
|
3150 | 'user': reviewer.get_api_data(include_secrets=False, | |
3151 | details='basic'), |
|
3151 | details='basic'), | |
|
3152 | 'reasons': reasons, | |||
3152 | 'review_status': st[0][1].status if st else 'not_reviewed', |
|
3153 | 'review_status': st[0][1].status if st else 'not_reviewed', | |
3153 | } |
|
3154 | } | |
3154 | for reviewer, st in pull_request.reviewers_statuses() |
|
3155 | for reviewer, reasons, st in pull_request.reviewers_statuses() | |
3155 | ] |
|
3156 | ] | |
3156 | } |
|
3157 | } | |
3157 |
|
3158 | |||
@@ -3203,9 +3204,23 b' class PullRequestReviewers(Base, BaseMod' | |||||
3203 | 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}, |
|
3204 | 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}, | |
3204 | ) |
|
3205 | ) | |
3205 |
|
3206 | |||
3206 | def __init__(self, user=None, pull_request=None): |
|
3207 | def __init__(self, user=None, pull_request=None, reasons=None): | |
3207 | self.user = user |
|
3208 | self.user = user | |
3208 | self.pull_request = pull_request |
|
3209 | self.pull_request = pull_request | |
|
3210 | self.reasons = reasons or [] | |||
|
3211 | ||||
|
3212 | @hybrid_property | |||
|
3213 | def reasons(self): | |||
|
3214 | if not self._reasons: | |||
|
3215 | return [] | |||
|
3216 | return self._reasons | |||
|
3217 | ||||
|
3218 | @reasons.setter | |||
|
3219 | def reasons(self, val): | |||
|
3220 | val = val or [] | |||
|
3221 | if any(not isinstance(x, basestring) for x in val): | |||
|
3222 | raise Exception('invalid reasons type, must be list of strings') | |||
|
3223 | self._reasons = val | |||
3209 |
|
3224 | |||
3210 | pull_requests_reviewers_id = Column( |
|
3225 | pull_requests_reviewers_id = Column( | |
3211 | 'pull_requests_reviewers_id', Integer(), nullable=False, |
|
3226 | 'pull_requests_reviewers_id', Integer(), nullable=False, | |
@@ -3215,8 +3230,9 b' class PullRequestReviewers(Base, BaseMod' | |||||
3215 | ForeignKey('pull_requests.pull_request_id'), nullable=False) |
|
3230 | ForeignKey('pull_requests.pull_request_id'), nullable=False) | |
3216 | user_id = Column( |
|
3231 | user_id = Column( | |
3217 | "user_id", Integer(), ForeignKey('users.user_id'), nullable=True) |
|
3232 | "user_id", Integer(), ForeignKey('users.user_id'), nullable=True) | |
3218 |
reason = Column( |
|
3233 | _reasons = Column( | |
3219 | UnicodeText().with_variant(UnicodeText(255), 'mysql'), nullable=True) |
|
3234 | 'reason', MutationList.as_mutable( | |
|
3235 | JsonType('list', dialect_map=dict(mysql=UnicodeText(16384))))) | |||
3220 |
|
3236 | |||
3221 | user = relationship('User') |
|
3237 | user = relationship('User') | |
3222 | pull_request = relationship('PullRequest') |
|
3238 | pull_request = relationship('PullRequest') |
@@ -26,7 +26,7 b' def upgrade(migrate_engine):' | |||||
26 | _reset_base(migrate_engine) |
|
26 | _reset_base(migrate_engine) | |
27 | from rhodecode.lib.dbmigrate.schema import db_4_5_0_0 |
|
27 | from rhodecode.lib.dbmigrate.schema import db_4_5_0_0 | |
28 |
|
28 | |||
29 | db_4_5_0_0.PullRequestReviewers.reason.create( |
|
29 | db_4_5_0_0.PullRequestReviewers.reasons.create( | |
30 | table=db_4_5_0_0.PullRequestReviewers.__table__) |
|
30 | table=db_4_5_0_0.PullRequestReviewers.__table__) | |
31 |
|
31 | |||
32 | def downgrade(migrate_engine): |
|
32 | def downgrade(migrate_engine): |
@@ -80,7 +80,7 b' class ChangesetStatusModel(BaseModel):' | |||||
80 | """ |
|
80 | """ | |
81 | votes = defaultdict(int) |
|
81 | votes = defaultdict(int) | |
82 | reviewers_number = len(statuses_by_reviewers) |
|
82 | reviewers_number = len(statuses_by_reviewers) | |
83 | for user, statuses in statuses_by_reviewers: |
|
83 | for user, reasons, statuses in statuses_by_reviewers: | |
84 | if statuses: |
|
84 | if statuses: | |
85 | ver, latest = statuses[0] |
|
85 | ver, latest = statuses[0] | |
86 | votes[latest.status] += 1 |
|
86 | votes[latest.status] += 1 | |
@@ -254,7 +254,7 b' class ChangesetStatusModel(BaseModel):' | |||||
254 | for x, y in (itertools.groupby(sorted(st, key=version), |
|
254 | for x, y in (itertools.groupby(sorted(st, key=version), | |
255 | version))] |
|
255 | version))] | |
256 |
|
256 | |||
257 |
pull_request_reviewers.append( |
|
257 | pull_request_reviewers.append((o.user, o.reasons, st)) | |
258 | return pull_request_reviewers |
|
258 | return pull_request_reviewers | |
259 |
|
259 | |||
260 | def calculated_review_status(self, pull_request, reviewers_statuses=None): |
|
260 | def calculated_review_status(self, pull_request, reviewers_statuses=None): |
@@ -59,7 +59,7 b' from rhodecode.lib.utils2 import (' | |||||
59 | str2bool, safe_str, get_commit_safe, safe_unicode, remove_prefix, md5_safe, |
|
59 | str2bool, safe_str, get_commit_safe, safe_unicode, remove_prefix, md5_safe, | |
60 | time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict, |
|
60 | time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict, | |
61 | glob2re) |
|
61 | glob2re) | |
62 | from rhodecode.lib.jsonalchemy import MutationObj, JsonType, JSONDict |
|
62 | from rhodecode.lib.jsonalchemy import MutationObj, MutationList, JsonType, JSONDict | |
63 | from rhodecode.lib.ext_json import json |
|
63 | from rhodecode.lib.ext_json import json | |
64 | from rhodecode.lib.caching_query import FromCache |
|
64 | from rhodecode.lib.caching_query import FromCache | |
65 | from rhodecode.lib.encrypt import AESCipher |
|
65 | from rhodecode.lib.encrypt import AESCipher | |
@@ -3149,9 +3149,10 b' class PullRequest(Base, _PullRequestBase' | |||||
3149 | { |
|
3149 | { | |
3150 | 'user': reviewer.get_api_data(include_secrets=False, |
|
3150 | 'user': reviewer.get_api_data(include_secrets=False, | |
3151 | details='basic'), |
|
3151 | details='basic'), | |
|
3152 | 'reasons': reasons, | |||
3152 | 'review_status': st[0][1].status if st else 'not_reviewed', |
|
3153 | 'review_status': st[0][1].status if st else 'not_reviewed', | |
3153 | } |
|
3154 | } | |
3154 | for reviewer, st in pull_request.reviewers_statuses() |
|
3155 | for reviewer, reasons, st in pull_request.reviewers_statuses() | |
3155 | ] |
|
3156 | ] | |
3156 | } |
|
3157 | } | |
3157 |
|
3158 | |||
@@ -3203,9 +3204,23 b' class PullRequestReviewers(Base, BaseMod' | |||||
3203 | 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}, |
|
3204 | 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}, | |
3204 | ) |
|
3205 | ) | |
3205 |
|
3206 | |||
3206 | def __init__(self, user=None, pull_request=None): |
|
3207 | def __init__(self, user=None, pull_request=None, reasons=None): | |
3207 | self.user = user |
|
3208 | self.user = user | |
3208 | self.pull_request = pull_request |
|
3209 | self.pull_request = pull_request | |
|
3210 | self.reasons = reasons or [] | |||
|
3211 | ||||
|
3212 | @hybrid_property | |||
|
3213 | def reasons(self): | |||
|
3214 | if not self._reasons: | |||
|
3215 | return [] | |||
|
3216 | return self._reasons | |||
|
3217 | ||||
|
3218 | @reasons.setter | |||
|
3219 | def reasons(self, val): | |||
|
3220 | val = val or [] | |||
|
3221 | if any(not isinstance(x, basestring) for x in val): | |||
|
3222 | raise Exception('invalid reasons type, must be list of strings') | |||
|
3223 | self._reasons = val | |||
3209 |
|
3224 | |||
3210 | pull_requests_reviewers_id = Column( |
|
3225 | pull_requests_reviewers_id = Column( | |
3211 | 'pull_requests_reviewers_id', Integer(), nullable=False, |
|
3226 | 'pull_requests_reviewers_id', Integer(), nullable=False, | |
@@ -3215,8 +3230,9 b' class PullRequestReviewers(Base, BaseMod' | |||||
3215 | ForeignKey('pull_requests.pull_request_id'), nullable=False) |
|
3230 | ForeignKey('pull_requests.pull_request_id'), nullable=False) | |
3216 | user_id = Column( |
|
3231 | user_id = Column( | |
3217 | "user_id", Integer(), ForeignKey('users.user_id'), nullable=True) |
|
3232 | "user_id", Integer(), ForeignKey('users.user_id'), nullable=True) | |
3218 |
reason = Column( |
|
3233 | _reasons = Column( | |
3219 | UnicodeText().with_variant(UnicodeText(255), 'mysql'), nullable=True) |
|
3234 | 'reason', MutationList.as_mutable( | |
|
3235 | JsonType('list', dialect_map=dict(mysql=UnicodeText(16384))))) | |||
3220 |
|
3236 | |||
3221 | user = relationship('User') |
|
3237 | user = relationship('User') | |
3222 | pull_request = relationship('PullRequest') |
|
3238 | pull_request = relationship('PullRequest') |
@@ -519,7 +519,12 b' def UserExtraIpForm():' | |||||
519 | return _UserExtraIpForm |
|
519 | return _UserExtraIpForm | |
520 |
|
520 | |||
521 |
|
521 | |||
|
522 | ||||
522 | def PullRequestForm(repo_id): |
|
523 | def PullRequestForm(repo_id): | |
|
524 | class ReviewerForm(formencode.Schema): | |||
|
525 | user_id = v.Int(not_empty=True) | |||
|
526 | reasons = All() | |||
|
527 | ||||
523 | class _PullRequestForm(formencode.Schema): |
|
528 | class _PullRequestForm(formencode.Schema): | |
524 | allow_extra_fields = True |
|
529 | allow_extra_fields = True | |
525 | filter_extra_fields = True |
|
530 | filter_extra_fields = True | |
@@ -531,8 +536,7 b' def PullRequestForm(repo_id):' | |||||
531 | target_ref = v.UnicodeString(strip=True, required=True) |
|
536 | target_ref = v.UnicodeString(strip=True, required=True) | |
532 | revisions = All(#v.NotReviewedRevisions(repo_id)(), |
|
537 | revisions = All(#v.NotReviewedRevisions(repo_id)(), | |
533 | v.UniqueList()(not_empty=True)) |
|
538 | v.UniqueList()(not_empty=True)) | |
534 | review_members = v.UniqueList(convert=int)(not_empty=True) |
|
539 | review_members = formencode.ForEach(ReviewerForm()) | |
535 |
|
||||
536 | pullrequest_title = v.UnicodeString(strip=True, required=True) |
|
540 | pullrequest_title = v.UnicodeString(strip=True, required=True) | |
537 | pullrequest_desc = v.UnicodeString(strip=True, required=False) |
|
541 | pullrequest_desc = v.UnicodeString(strip=True, required=False) | |
538 |
|
542 |
@@ -333,10 +333,18 b' class PullRequestModel(BaseModel):' | |||||
333 | Session().add(pull_request) |
|
333 | Session().add(pull_request) | |
334 | Session().flush() |
|
334 | Session().flush() | |
335 |
|
335 | |||
|
336 | reviewer_ids = set() | |||
336 | # members / reviewers |
|
337 | # members / reviewers | |
337 |
for |
|
338 | for reviewer_object in reviewers: | |
|
339 | if isinstance(reviewer_object, tuple): | |||
|
340 | user_id, reasons = reviewer_object | |||
|
341 | else: | |||
|
342 | user_id, reasons = reviewer_object, [] | |||
|
343 | ||||
338 | user = self._get_user(user_id) |
|
344 | user = self._get_user(user_id) | |
339 | reviewer = PullRequestReviewers(user, pull_request) |
|
345 | reviewer_ids.add(user.user_id) | |
|
346 | ||||
|
347 | reviewer = PullRequestReviewers(user, pull_request, reasons) | |||
340 | Session().add(reviewer) |
|
348 | Session().add(reviewer) | |
341 |
|
349 | |||
342 | # Set approval status to "Under Review" for all commits which are |
|
350 | # Set approval status to "Under Review" for all commits which are | |
@@ -348,7 +356,7 b' class PullRequestModel(BaseModel):' | |||||
348 | pull_request=pull_request |
|
356 | pull_request=pull_request | |
349 | ) |
|
357 | ) | |
350 |
|
358 | |||
351 | self.notify_reviewers(pull_request, reviewers) |
|
359 | self.notify_reviewers(pull_request, reviewer_ids) | |
352 | self._trigger_pull_request_hook( |
|
360 | self._trigger_pull_request_hook( | |
353 | pull_request, created_by_user, 'create') |
|
361 | pull_request, created_by_user, 'create') | |
354 |
|
362 | |||
@@ -570,6 +578,7 b' class PullRequestModel(BaseModel):' | |||||
570 | Session().commit() |
|
578 | Session().commit() | |
571 | self._trigger_pull_request_hook(pull_request, pull_request.author, |
|
579 | self._trigger_pull_request_hook(pull_request, pull_request.author, | |
572 | 'update') |
|
580 | 'update') | |
|
581 | ||||
573 | return (pull_request_version, changes) |
|
582 | return (pull_request_version, changes) | |
574 |
|
583 | |||
575 | def _create_version_from_snapshot(self, pull_request): |
|
584 | def _create_version_from_snapshot(self, pull_request): | |
@@ -711,8 +720,21 b' class PullRequestModel(BaseModel):' | |||||
711 | pull_request.updated_on = datetime.datetime.now() |
|
720 | pull_request.updated_on = datetime.datetime.now() | |
712 | Session().add(pull_request) |
|
721 | Session().add(pull_request) | |
713 |
|
722 | |||
714 |
def update_reviewers(self, pull_request, reviewer |
|
723 | def update_reviewers(self, pull_request, reviewer_data): | |
715 | reviewers_ids = set(reviewers_ids) |
|
724 | """ | |
|
725 | Update the reviewers in the pull request | |||
|
726 | ||||
|
727 | :param pull_request: the pr to update | |||
|
728 | :param reviewer_data: list of tuples [(user, ['reason1', 'reason2'])] | |||
|
729 | """ | |||
|
730 | ||||
|
731 | reviewers_reasons = {} | |||
|
732 | for user_id, reasons in reviewer_data: | |||
|
733 | if isinstance(user_id, (int, basestring)): | |||
|
734 | user_id = self._get_user(user_id).user_id | |||
|
735 | reviewers_reasons[user_id] = reasons | |||
|
736 | ||||
|
737 | reviewers_ids = set(reviewers_reasons.keys()) | |||
716 | pull_request = self.__get_pull_request(pull_request) |
|
738 | pull_request = self.__get_pull_request(pull_request) | |
717 | current_reviewers = PullRequestReviewers.query()\ |
|
739 | current_reviewers = PullRequestReviewers.query()\ | |
718 | .filter(PullRequestReviewers.pull_request == |
|
740 | .filter(PullRequestReviewers.pull_request == | |
@@ -728,7 +750,8 b' class PullRequestModel(BaseModel):' | |||||
728 | for uid in ids_to_add: |
|
750 | for uid in ids_to_add: | |
729 | changed = True |
|
751 | changed = True | |
730 | _usr = self._get_user(uid) |
|
752 | _usr = self._get_user(uid) | |
731 | reviewer = PullRequestReviewers(_usr, pull_request) |
|
753 | reasons = reviewers_reasons[uid] | |
|
754 | reviewer = PullRequestReviewers(_usr, pull_request, reasons) | |||
732 | Session().add(reviewer) |
|
755 | Session().add(reviewer) | |
733 |
|
756 | |||
734 | self.notify_reviewers(pull_request, ids_to_add) |
|
757 | self.notify_reviewers(pull_request, ids_to_add) |
@@ -145,7 +145,7 b' input.inline[type="file"] {' | |||||
145 | .alert { |
|
145 | .alert { | |
146 | margin: @padding 0; |
|
146 | margin: @padding 0; | |
147 | } |
|
147 | } | |
148 |
|
148 | |||
149 | .error-branding { |
|
149 | .error-branding { | |
150 | font-family: @text-semibold; |
|
150 | font-family: @text-semibold; | |
151 | color: @grey4; |
|
151 | color: @grey4; | |
@@ -1282,6 +1282,9 b' table.integrations {' | |||||
1282 | width: 100%; |
|
1282 | width: 100%; | |
1283 | overflow: auto; |
|
1283 | overflow: auto; | |
1284 | } |
|
1284 | } | |
|
1285 | .reviewer_reason { | |||
|
1286 | padding-left: 20px; | |||
|
1287 | } | |||
1285 | .reviewer_status { |
|
1288 | .reviewer_status { | |
1286 | display: inline-block; |
|
1289 | display: inline-block; | |
1287 | vertical-align: top; |
|
1290 | vertical-align: top; |
@@ -32,7 +32,7 b' var removeReviewMember = function(review' | |||||
32 | var obj = $('#reviewer_{0}_name'.format(reviewer_id)); |
|
32 | var obj = $('#reviewer_{0}_name'.format(reviewer_id)); | |
33 | obj.addClass('to-delete'); |
|
33 | obj.addClass('to-delete'); | |
34 | // now delete the input |
|
34 | // now delete the input | |
35 |
$('#reviewer_{0} |
|
35 | $('#reviewer_{0} input'.format(reviewer_id)).remove(); | |
36 | } |
|
36 | } | |
37 | } |
|
37 | } | |
38 | else{ |
|
38 | else{ | |
@@ -43,29 +43,36 b' var removeReviewMember = function(review' | |||||
43 | var addReviewMember = function(id, fname, lname, nname, gravatar_link, reasons) { |
|
43 | var addReviewMember = function(id, fname, lname, nname, gravatar_link, reasons) { | |
44 | var members = $('#review_members').get(0); |
|
44 | var members = $('#review_members').get(0); | |
45 | var reasons_html = ''; |
|
45 | var reasons_html = ''; | |
|
46 | var reasons_inputs = ''; | |||
|
47 | var reasons = reasons || []; | |||
46 | if (reasons) { |
|
48 | if (reasons) { | |
47 | for (var i = 0; i < reasons.length; i++) { |
|
49 | for (var i = 0; i < reasons.length; i++) { | |
48 | reasons_html += '<div class="reviewer_reason">- {0}</div>'.format( |
|
50 | reasons_html += '<div class="reviewer_reason">- {0}</div>'.format(reasons[i]); | |
49 | reasons[i] |
|
51 | reasons_inputs += '<input type="hidden" name="reason" value="' + escapeHtml(reasons[i]) + '">'; | |
50 | ); |
|
|||
51 | } |
|
52 | } | |
52 | } |
|
53 | } | |
53 | var tmpl = '<li id="reviewer_{2}">'+ |
|
54 | var tmpl = '<li id="reviewer_{2}">'+ | |
|
55 | '<input type="hidden" name="__start__" value="reviewer:mapping">'+ | |||
54 | '<div class="reviewer_status">'+ |
|
56 | '<div class="reviewer_status">'+ | |
55 | '<div class="flag_status not_reviewed pull-left reviewer_member_status"></div>'+ |
|
57 | '<div class="flag_status not_reviewed pull-left reviewer_member_status"></div>'+ | |
56 | '</div>'+ |
|
58 | '</div>'+ | |
57 | '<img alt="gravatar" class="gravatar" src="{0}"/>'+ |
|
59 | '<img alt="gravatar" class="gravatar" src="{0}"/>'+ | |
58 | '<span class="reviewer_name user">{1}</span>'+ |
|
60 | '<span class="reviewer_name user">{1}</span>'+ | |
59 | reasons_html + |
|
61 | reasons_html + | |
60 |
'<input type="hidden" value="{2}" |
|
62 | '<input type="hidden" name="user_id" value="{2}">'+ | |
|
63 | '<input type="hidden" name="__start__" value="reasons:sequence">'+ | |||
|
64 | '{3}'+ | |||
|
65 | '<input type="hidden" name="__end__" value="reasons:sequence">'+ | |||
61 | '<div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">' + |
|
66 | '<div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">' + | |
62 | '<i class="icon-remove-sign"></i>'+ |
|
67 | '<i class="icon-remove-sign"></i>'+ | |
63 | '</div>'+ |
|
68 | '</div>'+ | |
64 | '</div>'+ |
|
69 | '</div>'+ | |
|
70 | '<input type="hidden" name="__end__" value="reviewer:mapping">'+ | |||
65 | '</li>' ; |
|
71 | '</li>' ; | |
|
72 | ||||
66 | var displayname = "{0} ({1} {2})".format( |
|
73 | var displayname = "{0} ({1} {2})".format( | |
67 | nname, escapeHtml(fname), escapeHtml(lname)); |
|
74 | nname, escapeHtml(fname), escapeHtml(lname)); | |
68 | var element = tmpl.format(gravatar_link,displayname,id); |
|
75 | var element = tmpl.format(gravatar_link,displayname,id,reasons_inputs); | |
69 | // check if we don't have this ID already in |
|
76 | // check if we don't have this ID already in | |
70 | var ids = []; |
|
77 | var ids = []; | |
71 | var _els = $('#review_members li').toArray(); |
|
78 | var _els = $('#review_members li').toArray(); | |
@@ -83,7 +90,11 b' var _updatePullRequest = function(repo_n' | |||||
83 | var url = pyroutes.url( |
|
90 | var url = pyroutes.url( | |
84 | 'pullrequest_update', |
|
91 | 'pullrequest_update', | |
85 | {"repo_name": repo_name, "pull_request_id": pull_request_id}); |
|
92 | {"repo_name": repo_name, "pull_request_id": pull_request_id}); | |
86 | postData.csrf_token = CSRF_TOKEN; |
|
93 | if (typeof postData === 'string' ) { | |
|
94 | postData += '&csrf_token=' + CSRF_TOKEN; | |||
|
95 | } else { | |||
|
96 | postData.csrf_token = CSRF_TOKEN; | |||
|
97 | } | |||
87 | var success = function(o) { |
|
98 | var success = function(o) { | |
88 | window.location.reload(); |
|
99 | window.location.reload(); | |
89 | }; |
|
100 | }; | |
@@ -92,17 +103,9 b' var _updatePullRequest = function(repo_n' | |||||
92 |
|
103 | |||
93 | var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){ |
|
104 | var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){ | |
94 | if (reviewers_ids === undefined){ |
|
105 | if (reviewers_ids === undefined){ | |
95 | var reviewers_ids = []; |
|
106 | var postData = '_method=put&' + $('#reviewers input').serialize(); | |
96 | var ids = $('#review_members input').toArray(); |
|
107 | _updatePullRequest(repo_name, pull_request_id, postData); | |
97 | for(var i=0; i<ids.length;i++){ |
|
|||
98 | var id = ids[i].value |
|
|||
99 | reviewers_ids.push(id); |
|
|||
100 | } |
|
|||
101 | } |
|
108 | } | |
102 | var postData = { |
|
|||
103 | '_method':'put', |
|
|||
104 | 'reviewers_ids': reviewers_ids}; |
|
|||
105 | _updatePullRequest(repo_name, pull_request_id, postData); |
|
|||
106 | }; |
|
109 | }; | |
107 |
|
110 | |||
108 | /** |
|
111 | /** |
@@ -20,6 +20,9 b'' | |||||
20 | * turns objects into GET query string |
|
20 | * turns objects into GET query string | |
21 | */ |
|
21 | */ | |
22 | var toQueryString = function(o) { |
|
22 | var toQueryString = function(o) { | |
|
23 | if(typeof o === 'string') { | |||
|
24 | return o; | |||
|
25 | } | |||
23 | if(typeof o !== 'object') { |
|
26 | if(typeof o !== 'object') { | |
24 | return false; |
|
27 | return false; | |
25 | } |
|
28 | } |
@@ -111,7 +111,9 b'' | |||||
111 | </div> |
|
111 | </div> | |
112 | <div id="reviewers" class="block-right pr-details-content reviewers"> |
|
112 | <div id="reviewers" class="block-right pr-details-content reviewers"> | |
113 | ## members goes here, filled via JS based on initial selection ! |
|
113 | ## members goes here, filled via JS based on initial selection ! | |
|
114 | <input type="hidden" name="__start__" value="review_members:sequence"> | |||
114 | <ul id="review_members" class="group_members"></ul> |
|
115 | <ul id="review_members" class="group_members"></ul> | |
|
116 | <input type="hidden" name="__end__" value="review_members:sequence"> | |||
115 | <div id="add_reviewer_input" class='ac'> |
|
117 | <div id="add_reviewer_input" class='ac'> | |
116 | <div class="reviewer_ac"> |
|
118 | <div class="reviewer_ac"> | |
117 | ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))} |
|
119 | ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))} |
@@ -188,17 +188,27 b'' | |||||
188 | </div> |
|
188 | </div> | |
189 | <div id="reviewers" class="block-right pr-details-content reviewers"> |
|
189 | <div id="reviewers" class="block-right pr-details-content reviewers"> | |
190 | ## members goes here ! |
|
190 | ## members goes here ! | |
|
191 | <input type="hidden" name="__start__" value="review_members:sequence"> | |||
191 | <ul id="review_members" class="group_members"> |
|
192 | <ul id="review_members" class="group_members"> | |
192 | %for member,status in c.pull_request_reviewers: |
|
193 | %for member,reasons,status in c.pull_request_reviewers: | |
193 | <li id="reviewer_${member.user_id}"> |
|
194 | <li id="reviewer_${member.user_id}"> | |
194 | <div class="reviewers_member"> |
|
195 | <div class="reviewers_member"> | |
195 | <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}"> |
|
196 | <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}"> | |
196 | <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div> |
|
197 | <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div> | |
197 | </div> |
|
198 | </div> | |
198 | <div id="reviewer_${member.user_id}_name" class="reviewer_name"> |
|
199 | <div id="reviewer_${member.user_id}_name" class="reviewer_name"> | |
199 | ${self.gravatar_with_user(member.email, 16)} <div class="reviewer">(${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div> |
|
200 | ${self.gravatar_with_user(member.email, 16)} | |
200 | </div> |
|
201 | </div> | |
201 | <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" /> |
|
202 | <input type="hidden" name="__start__" value="reviewer:mapping"> | |
|
203 | <input type="hidden" name="__start__" value="reasons:sequence"> | |||
|
204 | %for reason in reasons: | |||
|
205 | <div class="reviewer_reason">- ${reason}</div> | |||
|
206 | <input type="hidden" name="reason" value="${reason}"> | |||
|
207 | ||||
|
208 | %endfor | |||
|
209 | <input type="hidden" name="__end__" value="reasons:sequence"> | |||
|
210 | <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="user_id" /> | |||
|
211 | <input type="hidden" name="__end__" value="reviewer:mapping"> | |||
202 | %if c.allowed_to_update: |
|
212 | %if c.allowed_to_update: | |
203 | <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;"> |
|
213 | <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;"> | |
204 | <i class="icon-remove-sign" ></i> |
|
214 | <i class="icon-remove-sign" ></i> | |
@@ -208,6 +218,7 b'' | |||||
208 | </li> |
|
218 | </li> | |
209 | %endfor |
|
219 | %endfor | |
210 | </ul> |
|
220 | </ul> | |
|
221 | <input type="hidden" name="__end__" value="review_members:sequence"> | |||
211 | %if not c.pull_request.is_closed(): |
|
222 | %if not c.pull_request.is_closed(): | |
212 | <div id="add_reviewer_input" class='ac' style="display: none;"> |
|
223 | <div id="add_reviewer_input" class='ac' style="display: none;"> | |
213 | %if c.allowed_to_update: |
|
224 | %if c.allowed_to_update: | |
@@ -569,7 +580,7 b'' | |||||
569 | $('.show-inline-comments').on('click', function(e){ |
|
580 | $('.show-inline-comments').on('click', function(e){ | |
570 | var boxid = $(this).attr('data-comment-id'); |
|
581 | var boxid = $(this).attr('data-comment-id'); | |
571 | var button = $(this); |
|
582 | var button = $(this); | |
572 |
|
583 | |||
573 | if(button.hasClass("comments-visible")) { |
|
584 | if(button.hasClass("comments-visible")) { | |
574 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ |
|
585 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ | |
575 | $(this).hide(); |
|
586 | $(this).hide(); |
@@ -256,8 +256,8 b' class TestPullrequestsController:' | |||||
256 | def test_comment_force_close_pull_request(self, pr_util, csrf_token): |
|
256 | def test_comment_force_close_pull_request(self, pr_util, csrf_token): | |
257 | pull_request = pr_util.create_pull_request() |
|
257 | pull_request = pr_util.create_pull_request() | |
258 | pull_request_id = pull_request.pull_request_id |
|
258 | pull_request_id = pull_request.pull_request_id | |
259 |
reviewers_ |
|
259 | reviewers_data = [(1, ['reason']), (2, ['reason2'])] | |
260 |
PullRequestModel().update_reviewers(pull_request_id, reviewers_ |
|
260 | PullRequestModel().update_reviewers(pull_request_id, reviewers_data) | |
261 | author = pull_request.user_id |
|
261 | author = pull_request.user_id | |
262 | repo = pull_request.target_repo.repo_id |
|
262 | repo = pull_request.target_repo.repo_id | |
263 | self.app.post( |
|
263 | self.app.post( | |
@@ -297,19 +297,27 b' class TestPullrequestsController:' | |||||
297 | url( |
|
297 | url( | |
298 | controller='pullrequests', |
|
298 | controller='pullrequests', | |
299 | action='create', |
|
299 | action='create', | |
300 |
repo_name=source.repo_name |
|
300 | repo_name=source.repo_name | |
301 |
|
|
301 | ), | |
302 | 'source_repo': source.repo_name, |
|
302 | [ | |
303 | 'source_ref': 'branch:default:' + commit_ids['change'], |
|
303 | ('source_repo', source.repo_name), | |
304 | 'target_repo': target.repo_name, |
|
304 | ('source_ref', 'branch:default:' + commit_ids['change']), | |
305 | 'target_ref': 'branch:default:' + commit_ids['ancestor'], |
|
305 | ('target_repo', target.repo_name), | |
306 | 'pullrequest_desc': 'Description', |
|
306 | ('target_ref', 'branch:default:' + commit_ids['ancestor']), | |
307 |
'pullrequest_ |
|
307 | ('pullrequest_desc', 'Description'), | |
308 |
' |
|
308 | ('pullrequest_title', 'Title'), | |
309 | 'revisions': commit_ids['change'], |
|
309 | ('__start__', 'review_members:sequence'), | |
310 | 'user': '', |
|
310 | ('__start__', 'reviewer:mapping'), | |
311 | 'csrf_token': csrf_token, |
|
311 | ('user_id', '1'), | |
312 | }, |
|
312 | ('__start__', 'reasons:sequence'), | |
|
313 | ('reason', 'Some reason'), | |||
|
314 | ('__end__', 'reasons:sequence'), | |||
|
315 | ('__end__', 'reviewer:mapping'), | |||
|
316 | ('__end__', 'review_members:sequence'), | |||
|
317 | ('revisions', commit_ids['change']), | |||
|
318 | ('user', ''), | |||
|
319 | ('csrf_token', csrf_token), | |||
|
320 | ], | |||
313 | status=302) |
|
321 | status=302) | |
314 |
|
322 | |||
315 | location = response.headers['Location'] |
|
323 | location = response.headers['Location'] | |
@@ -344,19 +352,27 b' class TestPullrequestsController:' | |||||
344 | url( |
|
352 | url( | |
345 | controller='pullrequests', |
|
353 | controller='pullrequests', | |
346 | action='create', |
|
354 | action='create', | |
347 |
repo_name=source.repo_name |
|
355 | repo_name=source.repo_name | |
348 |
|
|
356 | ), | |
349 | 'source_repo': source.repo_name, |
|
357 | [ | |
350 | 'source_ref': 'branch:default:' + commit_ids['change'], |
|
358 | ('source_repo', source.repo_name), | |
351 | 'target_repo': target.repo_name, |
|
359 | ('source_ref', 'branch:default:' + commit_ids['change']), | |
352 | 'target_ref': 'branch:default:' + commit_ids['ancestor-child'], |
|
360 | ('target_repo', target.repo_name), | |
353 | 'pullrequest_desc': 'Description', |
|
361 | ('target_ref', 'branch:default:' + commit_ids['ancestor-child']), | |
354 |
'pullrequest_ |
|
362 | ('pullrequest_desc', 'Description'), | |
355 |
' |
|
363 | ('pullrequest_title', 'Title'), | |
356 | 'revisions': commit_ids['change'], |
|
364 | ('__start__', 'review_members:sequence'), | |
357 | 'user': '', |
|
365 | ('__start__', 'reviewer:mapping'), | |
358 | 'csrf_token': csrf_token, |
|
366 | ('user_id', '2'), | |
359 | }, |
|
367 | ('__start__', 'reasons:sequence'), | |
|
368 | ('reason', 'Some reason'), | |||
|
369 | ('__end__', 'reasons:sequence'), | |||
|
370 | ('__end__', 'reviewer:mapping'), | |||
|
371 | ('__end__', 'review_members:sequence'), | |||
|
372 | ('revisions', commit_ids['change']), | |||
|
373 | ('user', ''), | |||
|
374 | ('csrf_token', csrf_token), | |||
|
375 | ], | |||
360 | status=302) |
|
376 | status=302) | |
361 |
|
377 | |||
362 | location = response.headers['Location'] |
|
378 | location = response.headers['Location'] | |
@@ -373,7 +389,8 b' class TestPullrequestsController:' | |||||
373 | assert len(notifications.all()) == 1 |
|
389 | assert len(notifications.all()) == 1 | |
374 |
|
390 | |||
375 | # Change reviewers and check that a notification was made |
|
391 | # Change reviewers and check that a notification was made | |
376 |
PullRequestModel().update_reviewers( |
|
392 | PullRequestModel().update_reviewers( | |
|
393 | pull_request.pull_request_id, [(1, [])]) | |||
377 | assert len(notifications.all()) == 2 |
|
394 | assert len(notifications.all()) == 2 | |
378 |
|
395 | |||
379 | def test_create_pull_request_stores_ancestor_commit_id(self, backend, |
|
396 | def test_create_pull_request_stores_ancestor_commit_id(self, backend, | |
@@ -397,19 +414,27 b' class TestPullrequestsController:' | |||||
397 | url( |
|
414 | url( | |
398 | controller='pullrequests', |
|
415 | controller='pullrequests', | |
399 | action='create', |
|
416 | action='create', | |
400 |
repo_name=source.repo_name |
|
417 | repo_name=source.repo_name | |
401 |
|
|
418 | ), | |
402 | 'source_repo': source.repo_name, |
|
419 | [ | |
403 | 'source_ref': 'branch:default:' + commit_ids['change'], |
|
420 | ('source_repo', source.repo_name), | |
404 | 'target_repo': target.repo_name, |
|
421 | ('source_ref', 'branch:default:' + commit_ids['change']), | |
405 | 'target_ref': 'branch:default:' + commit_ids['ancestor-child'], |
|
422 | ('target_repo', target.repo_name), | |
406 | 'pullrequest_desc': 'Description', |
|
423 | ('target_ref', 'branch:default:' + commit_ids['ancestor-child']), | |
407 |
'pullrequest_ |
|
424 | ('pullrequest_desc', 'Description'), | |
408 |
' |
|
425 | ('pullrequest_title', 'Title'), | |
409 | 'revisions': commit_ids['change'], |
|
426 | ('__start__', 'review_members:sequence'), | |
410 | 'user': '', |
|
427 | ('__start__', 'reviewer:mapping'), | |
411 | 'csrf_token': csrf_token, |
|
428 | ('user_id', '1'), | |
412 | }, |
|
429 | ('__start__', 'reasons:sequence'), | |
|
430 | ('reason', 'Some reason'), | |||
|
431 | ('__end__', 'reasons:sequence'), | |||
|
432 | ('__end__', 'reviewer:mapping'), | |||
|
433 | ('__end__', 'review_members:sequence'), | |||
|
434 | ('revisions', commit_ids['change']), | |||
|
435 | ('user', ''), | |||
|
436 | ('csrf_token', csrf_token), | |||
|
437 | ], | |||
413 | status=302) |
|
438 | status=302) | |
414 |
|
439 | |||
415 | location = response.headers['Location'] |
|
440 | location = response.headers['Location'] |
@@ -80,7 +80,7 b' def test_pull_request_stays_if_update_wi' | |||||
80 | voted_status, *pull_request.reviewers) |
|
80 | voted_status, *pull_request.reviewers) | |
81 |
|
81 | |||
82 | # Update, without change |
|
82 | # Update, without change | |
83 | PullRequestModel().update_commits(pull_request) |
|
83 | version, changes = PullRequestModel().update_commits(pull_request) | |
84 |
|
84 | |||
85 | # Expect that review status is the voted_status |
|
85 | # Expect that review status is the voted_status | |
86 | expected_review_status = voted_status |
|
86 | expected_review_status = voted_status | |
@@ -99,7 +99,7 b' def test_pull_request_under_review_if_up' | |||||
99 |
|
99 | |||
100 | # Update, with change |
|
100 | # Update, with change | |
101 | pr_util.update_source_repository() |
|
101 | pr_util.update_source_repository() | |
102 | PullRequestModel().update_commits(pull_request) |
|
102 | version, changes = PullRequestModel().update_commits(pull_request) | |
103 |
|
103 | |||
104 | # Expect that review status is the voted_status |
|
104 | # Expect that review status is the voted_status | |
105 | expected_review_status = db.ChangesetStatus.STATUS_UNDER_REVIEW |
|
105 | expected_review_status = db.ChangesetStatus.STATUS_UNDER_REVIEW |
@@ -115,7 +115,7 b' class TestPullRequestModel:' | |||||
115 |
|
115 | |||
116 | def test_get_awaiting_my_review(self, pull_request): |
|
116 | def test_get_awaiting_my_review(self, pull_request): | |
117 | PullRequestModel().update_reviewers( |
|
117 | PullRequestModel().update_reviewers( | |
118 | pull_request, [pull_request.author]) |
|
118 | pull_request, [(pull_request.author, ['author'])]) | |
119 | prs = PullRequestModel().get_awaiting_my_review( |
|
119 | prs = PullRequestModel().get_awaiting_my_review( | |
120 | pull_request.target_repo, user_id=pull_request.author.user_id) |
|
120 | pull_request.target_repo, user_id=pull_request.author.user_id) | |
121 | assert isinstance(prs, list) |
|
121 | assert isinstance(prs, list) | |
@@ -123,7 +123,7 b' class TestPullRequestModel:' | |||||
123 |
|
123 | |||
124 | def test_count_awaiting_my_review(self, pull_request): |
|
124 | def test_count_awaiting_my_review(self, pull_request): | |
125 | PullRequestModel().update_reviewers( |
|
125 | PullRequestModel().update_reviewers( | |
126 | pull_request, [pull_request.author]) |
|
126 | pull_request, [(pull_request.author, ['author'])]) | |
127 | pr_count = PullRequestModel().count_awaiting_my_review( |
|
127 | pr_count = PullRequestModel().count_awaiting_my_review( | |
128 | pull_request.target_repo, user_id=pull_request.author.user_id) |
|
128 | pull_request.target_repo, user_id=pull_request.author.user_id) | |
129 | assert pr_count == 1 |
|
129 | assert pr_count == 1 |
General Comments 0
You need to be logged in to leave comments.
Login now