Merge "Plugin endpoint parameters"
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
index 9f5bb48..3f4fee9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
@@ -26,8 +26,8 @@
import com.google.gerrit.server.group.InternalGroupDescription;
import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.Collection;
import org.eclipse.jgit.lib.ObjectId;
@@ -38,7 +38,7 @@
private final GroupControl.Factory groupControlFactory;
private final GroupCache groupCache;
private final Groups groups;
- private final Provider<ReviewDb> db;
+ private final SchemaFactory<ReviewDb> schema;
private final IncludingGroupMembership.Factory groupMembershipFactory;
@Inject
@@ -46,12 +46,12 @@
GroupControl.Factory groupControlFactory,
GroupCache groupCache,
Groups groups,
- Provider<ReviewDb> db,
+ SchemaFactory<ReviewDb> schema,
IncludingGroupMembership.Factory groupMembershipFactory) {
this.groupControlFactory = groupControlFactory;
this.groupCache = groupCache;
this.groups = groups;
- this.db = db;
+ this.schema = schema;
this.groupMembershipFactory = groupMembershipFactory;
}
@@ -72,9 +72,9 @@
@Override
public Collection<GroupReference> suggest(String name, ProjectState project) {
- try {
+ try (ReviewDb db = schema.open()) {
return groups
- .getAll(db.get())
+ .getAll(db)
.filter(group -> startsWithIgnoreCase(group, name))
.filter(this::isVisible)
.map(GroupReference::forGroup)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java
index cea0d25..5d60790 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java
@@ -213,10 +213,10 @@
}
Optional<AccountGroup> conflictingGroup;
- try {
+ try (ReviewDb db = schema.open()) {
conflictingGroup =
groups
- .getAll(schema.open())
+ .getAll(db)
.filter(group -> hasConfiguredName(byLowerCaseConfiguredName, group))
.findAny();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index 144ec1d..c103c89 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -88,7 +88,7 @@
@Deprecated static final Schema<ChangeData> V45 = schema(V44, ChangeField.REVERT_OF);
- static final Schema<ChangeData> V46 = schema(V45);
+ @Deprecated static final Schema<ChangeData> V46 = schema(V45);
// Removal of draft change workflow requires reindexing
static final Schema<ChangeData> V47 = schema(V46);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
index ad74108..c9a28f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -167,22 +167,18 @@
checkArgument(project != null, "project is required");
Change change = readOneReviewDbChange(db, changeId);
- if (change == null && args.migration.readChanges()) {
- // Change isn't in ReviewDb, but its primary storage might be in NoteDb.
- // Prepopulate the change exists with proper noteDbState field.
- change = newNoteDbOnlyChange(project, changeId);
- } else {
- checkNotNull(change, "change %s not found in ReviewDb", changeId);
- checkArgument(
- change.getProject().equals(project),
- "passed project %s when creating ChangeNotes for %s, but actual project is %s",
- project,
- changeId,
- change.getProject());
+ if (change == null) {
+ if (args.migration.readChanges()) {
+ return newNoteDbOnlyChange(project, changeId);
+ }
+ throw new NoSuchChangeException(changeId);
}
-
- // TODO: Throw NoSuchChangeException when the change is not found in the
- // database
+ checkArgument(
+ change.getProject().equals(project),
+ "passed project %s when creating ChangeNotes for %s, but actual project is %s",
+ project,
+ changeId,
+ change.getProject());
return change;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index 19190510..e92b003 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -35,7 +35,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_159> C = Schema_159.class;
+ public static final Class<Schema_160> C = Schema_160.class;
public static int getBinaryVersion() {
return guessVersion(C);
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index 3464fea..10f4b27 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -37,6 +37,7 @@
display: block;
}
.row {
+ align-items: center;
border-top: 1px solid #eee;
display: flex;
padding: .1em .25em;
@@ -44,6 +45,9 @@
:host(.loading) .row {
opacity: .5;
};
+ :host(.editLoaded) .hideOnEdit {
+ display: none;
+ }
.reviewed,
.status {
align-items: center;
@@ -107,14 +111,13 @@
font-family: var(--font-family-bold);
}
.show-hide {
- margin-left: .4em;
+ width: 1em;
}
.fileListButton {
margin: .5em;
}
.totalChanges {
justify-content: flex-end;
- padding-right: 2.6em;
text-align: right;
}
.warning {
@@ -129,7 +132,6 @@
display: block;
font-size: .8em;
min-width: 2em;
- margin-top: .1em;
}
gr-diff {
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
@@ -147,12 +149,39 @@
.mobile {
display: none;
}
- #container.editLoaded .hideOnEdit {
- display: none;
- }
.expandInline {
padding-right: .25em;
}
+ .reviewed {
+ margin-left: 2em;
+ width: 15em;
+ }
+ .reviewed label {
+ color: #2A66D9;
+ opacity: 0;
+ justify-content: flex-end;
+ width: 100%;
+ }
+ .reviewed label:hover {
+ cursor: pointer;
+ opacity: 100;
+ }
+ .row:hover .reviewed label,
+ .row:focus .reviewed label {
+ opacity: 100;
+ }
+ .reviewed input {
+ display: none;
+ }
+ .reviewedLabel {
+ color: rgba(0, 0, 0, .54);
+ margin-right: 1em;
+ opacity: 0;
+ }
+ .reviewedLabel.isReviewed {
+ display: initial;
+ opacity: 100;
+ }
@media screen and (max-width: 50em) {
.desktop {
display: none;
@@ -185,7 +214,6 @@
</style>
<div
id="container"
- class$="[[_computeContainerClass(editLoaded)]]"
on-tap="_handleFileListTap">
<template is="dom-repeat"
items="[[_shownFiles]]"
@@ -194,13 +222,14 @@
initial-count="[[fileListIncrement]]"
target-framerate="1">
<div class="file-row row" data-path$="[[file.__path]]" tabindex="-1">
- <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]" hidden>
- <input
- type="checkbox"
- checked="[[file.isReviewed]]"
- class="reviewed"
- aria-label="Reviewed checkbox"
- title="Mark as reviewed (shortcut: r)">
+ <div class="show-hide" hidden$="[[_userPrefs.expand_inline_diffs]]">
+ <label class="show-hide" data-path$="[[file.__path]]"
+ data-expand=true>
+ <input type="checkbox" class="show-hide"
+ checked$="[[_isFileExpanded(file.__path, _expandedFilePaths.*)]]"
+ data-path$="[[file.__path]]" data-expand=true>
+ [[_computeShowHideText(file.__path, _expandedFilePaths.*)]]
+ </label>
</div>
<div class$="[[_computeClass('status', file.__path)]]"
tabindex="0"
@@ -261,13 +290,11 @@
[[_formatPercentage(file.size, file.size_delta)]]
</span>
</div>
- <div class="show-hide" hidden$="[[_userPrefs.expand_inline_diffs]]">
- <label class="show-hide" data-path$="[[file.__path]]"
- data-expand=true>
- <input type="checkbox" class="show-hide"
- checked$="[[_isFileExpanded(file.__path, _expandedFilePaths.*)]]"
- data-path$="[[file.__path]]" data-expand=true>
- [[_computeShowHideText(file.__path, _expandedFilePaths.*)]]
+ <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]" hidden>
+ <span class$="reviewedLabel [[_computeReviewedClass(file.isReviewed)]]">Reviewed</span>
+ <label>
+ <input class="reviewed" type="checkbox" checked="[[file.isReviewed]]">
+ <span class="markReviewed" title="Mark as reviewed (shortcut: r)">[[_computeReviewedText(file.isReviewed)]]</span>
</label>
</div>
</div>
@@ -307,6 +334,8 @@
-[[_patchChange.deleted]]
</span>
</div>
+ <!-- Empty div here exists to keep spacing in sync with file rows. -->
+ <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]" hidden></div>
</div>
<div
class$="row totalChanges [[_computeExpandInlineClass(_userPrefs)]]"
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 100bdee..7807f5e 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -59,7 +59,10 @@
notify: true,
observer: '_updateDiffPreferences',
},
- editLoaded: Boolean,
+ editLoaded: {
+ type: Boolean,
+ observer: '_editLoadedChanged',
+ },
_files: {
type: Array,
observer: '_filesChanged',
@@ -424,9 +427,8 @@
row = row.parentElement;
}
const path = row.dataset.path;
-
// Handle checkbox mark as reviewed.
- if (e.target.classList.contains('reviewed')) {
+ if (e.target.classList.contains('markReviewed')) {
return this._reviewFile(path);
}
@@ -720,7 +722,7 @@
},
_computeShowHideText(path, expandedFilesRecord) {
- return this._isFileExpanded(path, expandedFilesRecord) ? '▼' : '◀';
+ return this._isFileExpanded(path, expandedFilesRecord) ? '▼' : '▶';
},
_computeFilesShown(numFilesShown, files) {
@@ -910,8 +912,16 @@
}, LOADING_DEBOUNCE_INTERVAL);
},
- _computeContainerClass(editLoaded) {
- return editLoaded ? 'editLoaded' : '';
+ _editLoadedChanged(editLoaded) {
+ this.classList.toggle('editLoaded', editLoaded);
+ },
+
+ _computeReviewedClass(isReviewed) {
+ return isReviewed ? 'isReviewed' : '';
+ },
+
+ _computeReviewedText(isReviewed) {
+ return isReviewed ? 'MARK UNREVIEWED' : 'MARK REVIEWED';
},
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index 4b407c4..e77ad2c 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -609,21 +609,29 @@
flushAsynchronousOperations();
const fileRows =
Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
- const commitMsg = fileRows[0].querySelector(
- 'input.reviewed[type="checkbox"]');
- const fileAdded = fileRows[1].querySelector(
- 'input.reviewed[type="checkbox"]');
- const myFile = fileRows[2].querySelector(
- 'input.reviewed[type="checkbox"]');
+ const checkSelector = 'input.reviewed[type="checkbox"]';
+ const commitMsg = fileRows[0].querySelector(checkSelector);
+ const fileAdded = fileRows[1].querySelector(checkSelector);
+ const myFile = fileRows[2].querySelector(checkSelector);
assert.isTrue(commitMsg.checked);
assert.isFalse(fileAdded.checked);
assert.isTrue(myFile.checked);
- MockInteractions.tap(commitMsg);
+ const commitReviewLabel = fileRows[0].querySelector('.reviewedLabel');
+ const markReviewLabel = commitMsg.nextElementSibling;
+ assert.isTrue(commitReviewLabel.classList.contains('isReviewed'));
+ assert.equal(markReviewLabel.textContent, 'MARK UNREVIEWED');
+
+ MockInteractions.tap(markReviewLabel);
assert.isTrue(saveStub.lastCall.calledWithExactly('/COMMIT_MSG', false));
- MockInteractions.tap(commitMsg);
+ assert.isFalse(commitReviewLabel.classList.contains('isReviewed'));
+ assert.equal(markReviewLabel.textContent, 'MARK REVIEWED');
+
+ MockInteractions.tap(markReviewLabel);
assert.isTrue(saveStub.lastCall.calledWithExactly('/COMMIT_MSG', true));
+ assert.isTrue(commitReviewLabel.classList.contains('isReviewed'));
+ assert.equal(markReviewLabel.textContent, 'MARK UNREVIEWED');
});
test('patch set from revisions', () => {
@@ -1207,12 +1215,15 @@
test('reviewed checkbox', () => {
const alertStub = sandbox.stub();
+ const saveReviewStub = sandbox.stub(element, '_saveReviewedState');
+
element.addEventListener('show-alert', alertStub);
element.editLoaded = false;
// Reviewed checkbox should be shown.
assert.isTrue(isVisible(element.$$('.reviewed')));
MockInteractions.pressAndReleaseKeyOn(element, 82, null, 'r');
assert.isFalse(alertStub.called);
+ assert.isTrue(saveReviewStub.calledOnce);
element.editLoaded = true;
flushAsynchronousOperations();
@@ -1220,6 +1231,7 @@
assert.isFalse(isVisible(element.$$('.reviewed')));
MockInteractions.pressAndReleaseKeyOn(element, 82, null, 'r');
assert.isTrue(alertStub.called);
+ assert.isTrue(saveReviewStub.calledOnce);
});
test('_getReviewedFiles does not call API', () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js
index 75b00e8..bfd8e90 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js
@@ -26,7 +26,6 @@
// NOTE: intended singleton.
value: {
- loaded: false,
loading: false,
callbacks: [],
},
@@ -34,9 +33,9 @@
},
get() {
- return new Promise(resolve => {
+ return new Promise((resolve, reject) => {
// If the lib is totally loaded, resolve immediately.
- if (this._state.loaded) {
+ if (this._getHighlightLib()) {
resolve(this._getHighlightLib());
return;
}
@@ -44,7 +43,7 @@
// If the library is not currently being loaded, then start loading it.
if (!this._state.loading) {
this._state.loading = true;
- this._loadHLJS().then(this._onLibLoaded.bind(this));
+ this._loadHLJS().then(this._onLibLoaded.bind(this)).catch(reject);
}
this._state.callbacks.push(resolve);
@@ -53,7 +52,6 @@
_onLibLoaded() {
const lib = this._getHighlightLib();
- this._state.loaded = true;
this._state.loading = false;
for (const cb of this._state.callbacks) {
cb(lib);
@@ -73,17 +71,28 @@
_getLibRoot() {
if (this._cachedLibRoot) { return this._cachedLibRoot; }
- return this._cachedLibRoot = document.head
- .querySelector('link[rel=import][href$="gr-app.html"]')
+ const appLink = document.head
+ .querySelector('link[rel=import][href$="gr-app.html"]');
+
+ if (!appLink) { return null; }
+
+ return this._cachedLibRoot = appLink
.href
.match(LIB_ROOT_PATTERN)[1];
},
_cachedLibRoot: null,
_loadHLJS() {
- return new Promise(resolve => {
+ return new Promise((resolve, reject) => {
const script = document.createElement('script');
- script.src = this._getLibRoot() + HLJS_PATH;
+ const src = this._getHLJSUrl();
+
+ if (!src) {
+ reject(new Error('Unable to load blank HLJS url.'));
+ return;
+ }
+
+ script.src = src;
script.onload = function() {
this._configureHighlightLib();
resolve();
@@ -91,5 +100,11 @@
Polymer.dom(document.head).appendChild(script);
});
},
+
+ _getHLJSUrl() {
+ const root = this._getLibRoot();
+ if (!root) { return null; }
+ return root + HLJS_PATH;
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html
index b46910a..6ddde46 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html
@@ -45,7 +45,6 @@
);
// Assert preconditions:
- assert.isFalse(element._state.loaded);
assert.isFalse(element._state.loading);
});
@@ -57,7 +56,6 @@
// Because the element state is a singleton, clean it up.
element._state.loading = false;
- element._state.loaded = false;
element._state.callbacks = [];
});
@@ -68,7 +66,6 @@
// It should now be in the loading state.
assert.isTrue(loadStub.called);
assert.isTrue(element._state.loading);
- assert.isFalse(element._state.loaded);
assert.isFalse(firstCallHandler.called);
const secondCallHandler = sinon.stub();
@@ -76,7 +73,6 @@
// No change in state.
assert.isTrue(element._state.loading);
- assert.isFalse(element._state.loaded);
assert.isFalse(firstCallHandler.called);
assert.isFalse(secondCallHandler.called);
@@ -85,11 +81,55 @@
flush(() => {
// The state should be loaded and both handlers called.
assert.isFalse(element._state.loading);
- assert.isTrue(element._state.loaded);
assert.isTrue(firstCallHandler.called);
assert.isTrue(secondCallHandler.called);
done();
});
});
+
+ suite('preloaded', () => {
+ setup(() => {
+ window.hljs = 'test-object';
+ });
+
+ teardown(() => {
+ delete window.hljs;
+ });
+
+ test('returns hljs', done => {
+ const firstCallHandler = sinon.stub();
+ element.get().then(firstCallHandler);
+ flush(() => {
+ assert.isTrue(firstCallHandler.called);
+ assert.isTrue(firstCallHandler.calledWith('test-object'));
+ done();
+ });
+ });
+ });
+
+ suite('_getHLJSUrl', () => {
+ suite('checking _getLibRoot', () => {
+ let libRootStub;
+ let root;
+
+ setup(() => {
+ libRootStub = sinon.stub(element, '_getLibRoot', () => root);
+ });
+
+ teardown(() => {
+ libRootStub.restore();
+ });
+
+ test('with no root', () => {
+ assert.isNull(element._getHLJSUrl());
+ });
+
+ test('with root', () => {
+ root = 'test-root.com/';
+ assert.equal(element._getHLJSUrl(),
+ 'test-root.com/bower_components/highlightjs/highlight.min.js');
+ });
+ });
+ });
});
</script>