Merge "Fix links in accounts REST API documentation"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 1a17305..b12b9be 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -653,6 +653,35 @@
}
@Test
+ public void removeReviewerNoVotes() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ gApi.changes()
+ .id(changeId)
+ .addReviewer(user.getId().toString());
+
+ // ReviewerState will vary between ReviewDb and NoteDb; we just care that it
+ // shows up somewhere.
+ Iterable<AccountInfo> reviewers = Iterables.concat(
+ gApi.changes().id(changeId).get().reviewers.values());
+ assertThat(reviewers).hasSize(1);
+ assertThat(reviewers.iterator().next()._accountId)
+ .isEqualTo(user.getId().get());
+
+ gApi.changes()
+ .id(changeId)
+ .reviewer(user.getId().toString())
+ .remove();
+ assertThat(gApi.changes().id(changeId).get().reviewers.isEmpty());
+
+ exception.expect(ResourceNotFoundException.class);
+ gApi.changes()
+ .id(changeId)
+ .reviewer(user.getId().toString())
+ .remove();
+ }
+
+ @Test
public void removeReviewer() throws Exception {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
index 28d7dcd8..a2a59e9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
@@ -128,6 +128,10 @@
public boolean updateChange(ChangeContext ctx)
throws AuthException, ResourceNotFoundException, OrmException {
Account.Id reviewerId = reviewer.getId();
+ if (!approvalsUtil.getReviewers(ctx.getDb(), ctx.getNotes()).all()
+ .contains(reviewerId)) {
+ throw new ResourceNotFoundException();
+ }
currChange = ctx.getChange();
currPs = psUtil.current(dbProvider.get(), ctx.getNotes());
@@ -156,9 +160,6 @@
throw new AuthException("delete not permitted");
}
}
- if (del.isEmpty()) {
- throw new ResourceNotFoundException();
- }
ctx.getDb().patchSetApprovals().delete(del);
ChangeUpdate update = ctx.getUpdate(currPs.getId());
update.removeReviewer(reviewerId);
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
index b176466..3702c84 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
@@ -14,7 +14,7 @@
(function(window) {
'use strict';
- var BOTTOM_OFFSET = 10;
+ var BOTTOM_OFFSET = 7.2; // Height of the arrow in tooltip.
/** @polymerBehavior Gerrit.TooltipBehavior */
var TooltipBehavior = {
@@ -84,29 +84,19 @@
},
_positionTooltip: function(tooltip) {
- var offset = this._getOffset(this);
- var top = offset.top;
- var left = offset.left;
-
- top -= this.offsetHeight + BOTTOM_OFFSET;
- left -= (tooltip.offsetWidth / 2) - (this.offsetWidth / 2);
- left = Math.max(0, left);
- top = Math.max(0, top);
-
- tooltip.style.left = left + 'px';
- tooltip.style.top = top + 'px';
- },
-
- _getOffset: function(el) {
- var top = 0;
- var left = 0;
- for (var node = el; node; node = node.offsetParent) {
- if (node.offsetTop) { top += node.offsetTop; }
- if (node.offsetLeft) { left += node.offsetLeft; }
+ var rect = this.getBoundingClientRect();
+ var boxRect = tooltip.getBoundingClientRect();
+ var parentRect = tooltip.parentElement.getBoundingClientRect();
+ var top = rect.top - parentRect.top - boxRect.height - BOTTOM_OFFSET;
+ var left =
+ rect.left - parentRect.left + (rect.width - boxRect.width) / 2;
+ if (left < 0) {
+ tooltip.updateStyles({
+ '--gr-tooltip-arrow-center-offset': left + 'px',
+ });
}
- top += window.scrollY;
- left += window.scrollX;
- return {top: top, left: left};
+ tooltip.style.left = Math.max(0, left) + 'px';
+ tooltip.style.top = Math.max(0, top) + 'px';
},
};
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index 19f9e85..a0a9305 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -17,6 +17,7 @@
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
+<link rel="import" href="../../shared/gr-label/gr-label.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -141,7 +142,12 @@
as="label">
<div class="labelValueContainer">
<span class$="[[label.className]]">
- <span class="labelValue">[[label.value]]</span>
+ <gr-label
+ has-tooltip
+ title="[[_computeValueTooltip(label.value, labelName)]]"
+ class="labelValue">
+ [[label.value]]
+ </gr-label>
<gr-account-link account="[[label.account]]"></gr-account-link>
</span>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index 379487d..e24cc4a 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -117,6 +117,11 @@
return result;
},
+ _computeValueTooltip: function(score, labelName) {
+ var values = this.change.labels[labelName].values;
+ return values[score];
+ },
+
_handleTopicChanged: function(e, topic) {
if (!topic.length) { topic = null; }
this.$.restAPI.setChangeTopic(this.change.id, topic);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index 557e7b8..832665b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -21,7 +21,9 @@
<div class="contentWrapper">
<content></content>
</div>
- <gr-diff-processor id="processor"></gr-diff-processor>
+ <gr-diff-processor
+ id="processor"
+ groups="{{_groups}}"></gr-diff-processor>
</template>
<script src="../gr-diff/gr-diff-line.js"></script>
<script src="../gr-diff/gr-diff-group.js"></script>
@@ -53,19 +55,34 @@
baseImage: Object,
revisionImage: Object,
_builder: Object,
+ _groups: Array,
},
get diffElement() {
return this.queryEffectiveChildren('#diffTable');
},
+ observers: [
+ '_groupsChanged(_groups.splices)',
+ ],
+
render: function(diff, comments, prefs) {
+ // Stop the processor (if it's running).
+ this.$.processor.cancel();
+
this._builder = this._getDiffBuilder(diff, comments, prefs);
this.$.processor.context = prefs.context;
this.$.processor.keyLocations = this._getCommentLocations(comments);
- this.$.processor.process(diff.content)
- .then(this._renderDiff.bind(this));
+
+ this._clearDiffContent();
+
+ this.$.processor.process(diff.content).then(function() {
+ if (this.isImageDiff) {
+ this._builder.renderDiffImages();
+ }
+ this.fire('render');
+ }.bind(this));
},
getLineElByChild: function(node) {
@@ -183,10 +200,6 @@
this._builder.emitGroup(group, sectionEl);
},
- emitDiff: function() {
- this._builder.emitDiff();
- },
-
showContext: function(newGroups, sectionEl) {
var groups = this._builder.groups;
// TODO(viktard): Polyfill findIndex for IE10.
@@ -219,19 +232,6 @@
throw Error('Unsupported diff view mode: ' + this.viewMode);
},
- _renderDiff: function(groups) {
- this._builder.groups = groups;
-
- this._clearDiffContent();
- this.emitDiff();
- if (this.isImageDiff) {
- this._builder.renderDiffImages();
- }
- this.async(function() {
- this.fire('render');
- }, 1);
- },
-
_clearDiffContent: function() {
this.diffElement.innerHTML = null;
},
@@ -252,6 +252,18 @@
}
return result;
},
+
+ _groupsChanged: function(changeRecord) {
+ if (!changeRecord) { return; }
+ changeRecord.indexSplices.forEach(function(splice) {
+ var group;
+ for (var i = 0; i < splice.addedCount; i++) {
+ group = splice.object[splice.index + i];
+ this._builder.groups.push(group);
+ this._builder.emitGroup(group);
+ }
+ }, this);
+ },
});
})();
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
index b1256db..f68825c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
@@ -59,12 +59,6 @@
var PARTIAL_CONTEXT_AMOUNT = 10;
- GrDiffBuilder.prototype.emitDiff = function() {
- for (var i = 0; i < this.groups.length; i++) {
- this.emitGroup(this.groups[i]);
- }
- };
-
GrDiffBuilder.prototype.buildSectionElement = function(group) {
throw Error('Subclasses must implement buildGroupElement');
};
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
index 60654a8..4c5ee62 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
@@ -98,28 +98,39 @@
assert.equal(cursorElement.diffRow, firstDeltaRow);
});
- test('diff cursor functionality (unified)', function() {
- diffElement.viewMode = 'UNIFIED_DIFF';
- cursorElement.reInitCursor();
+ suite('unified diff', function() {
- // The cursor has been initialized to the first delta.
- assert.isOk(cursorElement.diffRow);
+ setup(function(done) {
+ // We must allow the diff to re-render after setting the viewMode.
+ var renderHandler = function() {
+ diffElement.removeEventListener('render', renderHandler);
+ cursorElement.reInitCursor();
+ done();
+ };
+ diffElement.addEventListener('render', renderHandler);
+ diffElement.viewMode = 'UNIFIED_DIFF';
+ });
- var firstDeltaRow = diffElement.$$('.section.delta .diff-row');
- assert.equal(cursorElement.diffRow, firstDeltaRow);
+ test('diff cursor functionality (unified)', function() {
+ // The cursor has been initialized to the first delta.
+ assert.isOk(cursorElement.diffRow);
- firstDeltaRow = diffElement.$$('.section.delta .diff-row');
- assert.equal(cursorElement.diffRow, firstDeltaRow);
+ var firstDeltaRow = diffElement.$$('.section.delta .diff-row');
+ assert.equal(cursorElement.diffRow, firstDeltaRow);
- cursorElement.moveDown();
+ firstDeltaRow = diffElement.$$('.section.delta .diff-row');
+ assert.equal(cursorElement.diffRow, firstDeltaRow);
- assert.notEqual(cursorElement.diffRow, firstDeltaRow);
- assert.equal(cursorElement.diffRow, firstDeltaRow.nextSibling);
+ cursorElement.moveDown();
- cursorElement.moveUp();
+ assert.notEqual(cursorElement.diffRow, firstDeltaRow);
+ assert.equal(cursorElement.diffRow, firstDeltaRow.nextSibling);
- assert.notEqual(cursorElement.diffRow, firstDeltaRow.nextSibling);
- assert.equal(cursorElement.diffRow, firstDeltaRow);
+ cursorElement.moveUp();
+
+ assert.notEqual(cursorElement.diffRow, firstDeltaRow.nextSibling);
+ assert.equal(cursorElement.diffRow, firstDeltaRow);
+ });
});
test('cursor side functionality', function() {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
index 0a54665..f0fc649 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
@@ -58,6 +58,8 @@
type: Object,
value: function() { return {left: {}, right: {}}; },
},
+
+ _nextStepHandle: Number,
},
/**
@@ -82,6 +84,7 @@
// If we are done, resolve the promise.
if (state.sectionIndex >= content.length) {
resolve(this.groups);
+ this._nextStepHandle = undefined;
return;
}
@@ -95,7 +98,7 @@
// Increment the index and recurse.
state.sectionIndex++;
- this.async(nextStep, 1);
+ this._nextStepHandle = this.async(nextStep, 1);
};
nextStep.call(this);
@@ -103,6 +106,16 @@
},
/**
+ * Cancel any jobs that are running.
+ */
+ cancel: function() {
+ if (this._nextStepHandle !== undefined) {
+ this.cancelAsync(this._nextStepHandle);
+ this._nextStepHandle = undefined;
+ }
+ },
+
+ /**
* Process the next section of the diff.
*/
_processNext: function(state, content) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 32de7d5..d4cced0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -36,7 +36,10 @@
changeNum: String,
patchRange: Object,
path: String,
- prefs: Object,
+ prefs: {
+ type: Object,
+ observer: '_prefsObserver',
+ },
projectConfig: {
type: Object,
observer: '_projectConfigChanged',
@@ -57,6 +60,7 @@
viewMode: {
type: String,
value: DiffViewMode.SIDE_BY_SIDE,
+ observer: '_viewModeObserver',
},
_diff: Object,
_comments: Object,
@@ -64,10 +68,6 @@
_revisionImage: Object,
},
- observers: [
- '_prefsChanged(prefs.*, viewMode)',
- ],
-
listeners: {
'thread-discard': '_handleThreadDiscard',
'comment-discard': '_handleCommentDiscard',
@@ -303,8 +303,28 @@
});
},
- _prefsChanged: function(prefsChangeRecord) {
- var prefs = prefsChangeRecord.base;
+ _prefsObserver: function(newPrefs, oldPrefs) {
+ // Scan the preference objects one level deep to see if they differ.
+ var differ = !oldPrefs;
+ if (newPrefs && oldPrefs) {
+ for (var key in newPrefs) {
+ if (newPrefs[key] !== oldPrefs[key]) {
+ differ = true;
+ }
+ }
+ }
+
+ if (differ) {
+ this._prefsChanged(newPrefs);
+ }
+ },
+
+ _viewModeObserver: function() {
+ this._prefsChanged(this.prefs);
+ },
+
+ _prefsChanged: function(prefs) {
+ if (!prefs) { return; }
this.customStyle['--content-width'] = prefs.line_length + 'ch';
this.updateStyles();
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index 5936bee..c12d653 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -42,7 +42,7 @@
getAccount: function() {
return Promise.resolve({name: 'Judy Hopps'});
},
- })
+ });
element = fixture('basic');
errorStub = sinon.stub(console, 'error');
Gerrit.install(function(p) { plugin = p; }, '0.1',
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label.html b/polygerrit-ui/app/elements/shared/gr-label/gr-label.html
new file mode 100644
index 0000000..04b12e7
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.html
@@ -0,0 +1,23 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
+<dom-module id="gr-label">
+ <template strip-whitespace>
+ <content></content>
+ </template>
+ <script src="gr-label.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
new file mode 100644
index 0000000..37e1f77
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-label',
+
+ behaviors: [
+ Gerrit.TooltipBehavior,
+ ],
+ });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html
index 64ef9b2..57a7272 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html
@@ -20,7 +20,8 @@
<template>
<style>
:host {
- --gr-tooltip-arrow-size: .6em;
+ --gr-tooltip-arrow-size: .5em;
+ --gr-tooltip-arrow-center-offset: 0;
background-color: #333;
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
@@ -38,6 +39,7 @@
height: 0;
position: absolute;
left: calc(50% - var(--gr-tooltip-arrow-size));
+ margin-left: var(--gr-tooltip-arrow-center-offset);
width: 0;
}
</style>
@@ -46,4 +48,3 @@
</template>
<script src="gr-tooltip.js"></script>
</dom-module>
-