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