Merge "Fix null view mode for expanding diffs"
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
index 159966a..47fef6d 100644
--- a/Documentation/database-setup.txt
+++ b/Documentation/database-setup.txt
@@ -55,6 +55,8 @@
[[createdb_mysql]]
=== MySQL
+Requirements: MySQL version 5.1 or later.
+
This option is also more complicated than the H2 option. Just as with
PostgreSQL it's also recommended for larger installations.
@@ -77,6 +79,8 @@
[[createdb_mariadb]]
=== MariaDB
+Requirements: MariaDB version 5.5 or later.
+
Refer to MySQL section above how to create MariaDB database.
Visit MariaDB's link:https://mariadb.com/kb/en/mariadb/[documentation] for further
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index bd7e7a5..ad2c50c 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -2389,7 +2389,7 @@
and link:https://gerrit.googlesource.com/plugins/metrics-reporter-graphite/[Graphite].
There is also a working example of reporting metrics to the console in the
-link:https://gerrit.googlesource.com/plugins/cookbook-plugin/+/master/src/main/java/com/googlesource/gerrit/plugins/cookbook/ConsoleMetricReporter.jave[
+link:https://gerrit.googlesource.com/plugins/cookbook-plugin/+/master/src/main/java/com/googlesource/gerrit/plugins/cookbook/ConsoleMetricReporter.java[
cookbook plugin].
=== Providing own metrics
diff --git a/Documentation/note-db.txt b/Documentation/note-db.txt
index 728d630..815cda8 100644
--- a/Documentation/note-db.txt
+++ b/Documentation/note-db.txt
@@ -137,14 +137,23 @@
To continue with the full migration after running the trial migration, use
either the online or offline migration steps as normal. To revert to
ReviewDb-only, remove `noteDb.changes.read` and `noteDb.changes.write` from
-`gerrit.config` and restart Gerrit.
+`notedb.config` and restart Gerrit.
== Configuration
-The migration process works by setting a configuration option in `gerrit.config`
+The migration process works by setting a configuration option in `notedb.config`
for each step in the process, then performing the corresponding data migration.
-In general, users should not set these options manually; this section serves
-primarily as a reference.
+
+Config options are read from `notedb.config` first, falling back to
+`gerrit.config`. If editing config manually, you may edit either file, but the
+migration process itself only touches `notedb.config`. This means if your
+`gerrit.config` is managed with Puppet or a similar tool, it can overwrite
+`gerrit.config` without affecting the migration process. You should not manage
+`notedb.config` with Puppet, but you may copy values back into `gerrit.config`
+and delete `notedb.config` at some later point after completing the migration.
+
+In general, users should not set the options described below manually; this
+section serves primarily as a reference.
- `noteDb.changes.write=true`: During a ReviewDb write, the state of the change
in NoteDb is written to the `note_db_state` field in the `Change` entity.
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 77eb9af..11277f3 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1112,6 +1112,73 @@
}
----
+
+[[create-access-change]]
+=== Create Access Rights Change for review.
+--
+'PUT /projects/link:rest-api-projects.html#project-name[\{project-name\}]/access:review
+--
+
+Sets access rights for the project using the diff schema provided by
+link:#project-access-input[ProjectAccessInput]
+
+This takes the same input as link:#set-access[Update Access Rights], but creates a pending
+change for review. Like link:#create-change[Create Change], it returns
+a link:#change-info[ChangeInfo] entity describing the resulting change.
+
+.Request
+----
+ PUT /projects/MyProject/access:review HTTP/1.0
+ Content-Type: application/json; charset=UTF-8
+
+ {
+ "add":{
+ "refs/heads/*":{
+ "permissions":{
+ "read":{
+ "rules":{
+ "global:Anonymous-Users": {
+ "action":"DENY",
+ "force":false
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+----
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "id": "testproj~refs%2Fmeta%2Fconfig~Ieaf185bf90a1fc3b58461e399385e158a20b31a2",
+ "project": "testproj",
+ "branch": "refs/meta/config",
+ "hashtags": [],
+ "change_id": "Ieaf185bf90a1fc3b58461e399385e158a20b31a2",
+ "subject": "Review access change",
+ "status": "NEW",
+ "created": "2017-09-07 14:31:11.852000000",
+ "updated": "2017-09-07 14:31:11.852000000",
+ "submit_type": "CHERRY_PICK",
+ "mergeable": true,
+ "insertions": 2,
+ "deletions": 0,
+ "unresolved_comment_count": 0,
+ "has_review_started": true,
+ "_number": 7,
+ "owner": {
+ "_account_id": 1000000
+ }
+ }
+----
+
+
[[index]]
=== Index all changes in a project
diff --git a/WORKSPACE b/WORKSPACE
index d76b8d4..5f3d1e9 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -716,18 +716,18 @@
sha1 = "4785a3c21320980282f9f33d0d1264a69040538f",
)
-TRUTH_VERS = "0.34"
+TRUTH_VERS = "0.35"
maven_jar(
name = "truth",
artifact = "com.google.truth:truth:" + TRUTH_VERS,
- sha1 = "73379200e907386d27eb10da60c5c2ed339f2bec",
+ sha1 = "c08a7fde45e058323bcfa3f510d4fe1e2b028f37",
)
maven_jar(
name = "truth-java8-extension",
artifact = "com.google.truth.extensions:truth-java8-extension:" + TRUTH_VERS,
- sha1 = "2d4aa36f7c048805af211406c4da4ddd1de15474",
+ sha1 = "5457fdf91b1e954b070ad7f2db9bea5505da4bca",
)
# When bumping the easymock version number, make sure to also move powermock to a compatible version
diff --git a/gerrit-acceptance-framework/BUILD b/gerrit-acceptance-framework/BUILD
index 5a7f3b7..8a95346 100644
--- a/gerrit-acceptance-framework/BUILD
+++ b/gerrit-acceptance-framework/BUILD
@@ -1,6 +1,12 @@
load("//tools/bzl:java.bzl", "java_library2")
+load("//tools/bzl:junit.bzl", "junit_tests")
-SRCS = glob(["src/test/java/com/google/gerrit/acceptance/*.java"])
+TEST_SRCS = ["src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java"]
+
+SRCS = glob(
+ ["src/test/java/com/google/gerrit/acceptance/*.java"],
+ exclude = TEST_SRCS,
+)
PROVIDED = [
"//gerrit-common:annotations",
@@ -77,3 +83,14 @@
title = "Gerrit Acceptance Test Framework Documentation",
visibility = ["//visibility:public"],
)
+
+junit_tests(
+ name = "acceptance_framework_tests",
+ srcs = TEST_SRCS,
+ deps = [
+ ":lib",
+ "//lib:guava",
+ "//lib:truth",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 6a1e3b9..0db732b 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -253,6 +253,7 @@
@Inject private InProcessProtocol inProcessProtocol;
@Inject private Provider<AnonymousUser> anonymousUser;
@Inject private SchemaFactory<ReviewDb> reviewDbProvider;
+ @Inject private ChangeControl.GenericFactory changeControlFactory;
private List<Repository> toClose;
@@ -1116,9 +1117,10 @@
}
protected ChangeResource parseChangeResource(String changeId) throws Exception {
- List<ChangeControl> ctls = changeFinder.find(changeId, atrScope.get().getUser());
- assertThat(ctls).hasSize(1);
- return changeResourceFactory.create(ctls.get(0));
+ List<ChangeNotes> notes = changeFinder.find(changeId);
+ assertThat(notes).hasSize(1);
+ return changeResourceFactory.create(
+ changeControlFactory.controlFor(notes.get(0), atrScope.get().getUser()));
}
protected String createGroup(String name) throws Exception {
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
index c651d48..bd4f653 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
@@ -40,7 +40,7 @@
}
for (String section : s.getSections()) {
for (String subsection : s.getSubsections(section)) {
- for (String name : s.getNames(section, subsection)) {
+ for (String name : s.getNames(section, subsection, true)) {
setStringList(
section,
subsection,
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
new file mode 100644
index 0000000..49c23e3
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
@@ -0,0 +1,118 @@
+// Copyright (C) 2017 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.
+
+package com.google.gerrit.acceptance;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Files;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.util.FS;
+import org.junit.Test;
+
+public class MergeableFileBasedConfigTest {
+ @Test
+ public void mergeNull() throws Exception {
+ MergeableFileBasedConfig cfg = newConfig();
+ cfg.setString("foo", null, "bar", "value");
+ String expected = "[foo]\n\tbar = value\n";
+ assertConfig(cfg, expected);
+ cfg.merge(null);
+ assertConfig(cfg, expected);
+ }
+
+ @Test
+ public void mergeFlatConfig() throws Exception {
+ MergeableFileBasedConfig cfg = newConfig();
+ cfg.setString("foo", null, "bar1", "value1");
+ cfg.setString("foo", null, "bar2", "value2");
+ cfg.setString("foo", "sub", "bar1", "value1");
+ cfg.setString("foo", "sub", "bar2", "value2");
+
+ assertConfig(
+ cfg,
+ "[foo]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = value2\n"
+ + "[foo \"sub\"]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = value2\n");
+
+ Config toMerge = new Config();
+ toMerge.setStringList("foo", null, "bar2", ImmutableList.of("merge1", "merge2"));
+ toMerge.setStringList("foo", "sub", "bar2", ImmutableList.of("merge1", "merge2"));
+ cfg.merge(toMerge);
+
+ assertConfig(
+ cfg,
+ "[foo]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = merge1\n"
+ + "\tbar2 = merge2\n"
+ + "[foo \"sub\"]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = merge1\n"
+ + "\tbar2 = merge2\n");
+ }
+
+ @Test
+ public void mergeStackedConfig() throws Exception {
+ MergeableFileBasedConfig cfg = newConfig();
+ cfg.setString("foo", null, "bar1", "value1");
+ cfg.setString("foo", null, "bar2", "value2");
+ cfg.setString("foo", "sub", "bar1", "value1");
+ cfg.setString("foo", "sub", "bar2", "value2");
+
+ assertConfig(
+ cfg,
+ "[foo]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = value2\n"
+ + "[foo \"sub\"]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = value2\n");
+
+ Config base = new Config();
+ Config toMerge = new Config(base);
+ base.setStringList("foo", null, "bar2", ImmutableList.of("merge1", "merge2"));
+ base.setStringList("foo", "sub", "bar2", ImmutableList.of("merge1", "merge2"));
+ cfg.merge(toMerge);
+
+ assertConfig(
+ cfg,
+ "[foo]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = merge1\n"
+ + "\tbar2 = merge2\n"
+ + "[foo \"sub\"]\n"
+ + "\tbar1 = value1\n"
+ + "\tbar2 = merge1\n"
+ + "\tbar2 = merge2\n");
+ }
+
+ private MergeableFileBasedConfig newConfig() throws Exception {
+ File f = File.createTempFile(getClass().getSimpleName(), ".config");
+ f.deleteOnExit();
+ return new MergeableFileBasedConfig(f, FS.detect());
+ }
+
+ private void assertConfig(MergeableFileBasedConfig cfg, String expected) throws Exception {
+ assertThat(cfg.toText()).isEqualTo(expected);
+ cfg.save();
+ assertThat(new String(Files.readAllBytes(cfg.getFile().toPath()), UTF_8)).isEqualTo(expected);
+ }
+}
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
index 03dcbca..5fd8da2 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -480,6 +480,11 @@
assertThat(message(refUpdate).toLowerCase()).contains(expectedMessage.toLowerCase());
}
+ public void assertNotMessage(String message) {
+ RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
+ assertThat(message(refUpdate).toLowerCase()).doesNotContain(message.toLowerCase());
+ }
+
public String getMessage() {
RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
return message(refUpdate);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/AbandonIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/AbandonIT.java
index cb9d705..52e1ea4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/AbandonIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/AbandonIT.java
@@ -33,7 +33,6 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.AbandonUtil;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testutil.TestTimeUtil;
import com.google.inject.Inject;
@@ -64,14 +63,10 @@
public void batchAbandon() throws Exception {
CurrentUser user = atrScope.get().getUser();
PushOneCommit.Result a = createChange();
- List<ChangeControl> controlA = changeFinder.find(a.getChangeId(), user);
- assertThat(controlA).hasSize(1);
PushOneCommit.Result b = createChange();
- List<ChangeControl> controlB = changeFinder.find(b.getChangeId(), user);
- assertThat(controlB).hasSize(1);
- List<ChangeControl> list = ImmutableList.of(controlA.get(0), controlB.get(0));
+ List<ChangeData> list = ImmutableList.of(a.getChange(), b.getChange());
changeAbandoner.batchAbandon(
- batchUpdateFactory, controlA.get(0).getProject().getNameKey(), user, list, "deadbeef");
+ batchUpdateFactory, a.getChange().project(), user, list, "deadbeef");
ChangeInfo info = get(a.getChangeId());
assertThat(info.status).isEqualTo(ChangeStatus.ABANDONED);
@@ -95,12 +90,8 @@
CurrentUser user = atrScope.get().getUser();
PushOneCommit.Result a = createChange(project1, "master", "x", "x", "x", "");
- List<ChangeControl> controlA = changeFinder.find(a.getChangeId(), user);
- assertThat(controlA).hasSize(1);
PushOneCommit.Result b = createChange(project2, "master", "x", "x", "x", "");
- List<ChangeControl> controlB = changeFinder.find(b.getChangeId(), user);
- assertThat(controlB).hasSize(1);
- List<ChangeControl> list = ImmutableList.of(controlA.get(0), controlB.get(0));
+ List<ChangeData> list = ImmutableList.of(a.getChange(), b.getChange());
exception.expect(ResourceConflictException.class);
exception.expectMessage(
String.format("Project name \"%s\" doesn't match \"%s\"", project2Name, project1Name));
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 4ce412c..8041e9f 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
@@ -116,7 +116,6 @@
import com.google.gerrit.server.git.ChangeMessageModifier;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.Util;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
@@ -3304,9 +3303,7 @@
}
private ChangeResource parseResource(PushOneCommit.Result r) throws Exception {
- List<ChangeControl> ctls = changeFinder.find(r.getChangeId(), atrScope.get().getUser());
- assertThat(ctls).hasSize(1);
- return changeResourceFactory.create(ctls.get(0));
+ return parseChangeResource(r.getChangeId());
}
private Optional<ReviewerState> getReviewerState(String changeId, Account.Id accountId)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 0dc1389..930b996 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -256,7 +256,7 @@
@Test
public void output() throws Exception {
- String url = canonicalWebUrl.get();
+ String url = canonicalWebUrl.get() + "#/c/" + project.get() + "/+/";
ObjectId initialHead = testRepo.getRepository().resolve("HEAD");
PushOneCommit.Result r1 = pushTo("refs/for/master");
Change.Id id1 = r1.getChange().getId();
@@ -298,6 +298,22 @@
}
@Test
+ public void autoclose() throws Exception {
+ // Create a change
+ PushOneCommit.Result r = pushTo("refs/for/master");
+ r.assertOkStatus();
+
+ // Force push it, closing it
+ String master = "refs/heads/master";
+ assertPushOk(pushHead(testRepo, master, false), master);
+
+ // Attempt to push amended commit to same change
+ String url = canonicalWebUrl.get() + "#/c/" + project.get() + "/+/" + r.getChange().getId();
+ r = amendChange(r.getChangeId(), "refs/for/master");
+ r.assertErrorStatus("change " + url + " closed");
+ }
+
+ @Test
public void pushForMasterWithTopic() throws Exception {
// specify topic in ref
String topic = "my/topic";
@@ -486,26 +502,31 @@
// Push a private change.
PushOneCommit.Result r = pushTo("refs/for/master%private");
r.assertOkStatus();
+ r.assertMessage(" [PRIVATE]");
assertThat(r.getChange().change().isPrivate()).isTrue();
// Pushing a new patch set without --private doesn't remove the privacy flag from the change.
r = amendChange(r.getChangeId(), "refs/for/master");
r.assertOkStatus();
+ r.assertMessage(" [PRIVATE]");
assertThat(r.getChange().change().isPrivate()).isTrue();
// Remove the privacy flag from the change.
r = amendChange(r.getChangeId(), "refs/for/master%remove-private");
r.assertOkStatus();
+ r.assertNotMessage(" [PRIVATE]");
assertThat(r.getChange().change().isPrivate()).isFalse();
// Normal push: privacy flag is not added back.
r = amendChange(r.getChangeId(), "refs/for/master");
r.assertOkStatus();
+ r.assertNotMessage(" [PRIVATE]");
assertThat(r.getChange().change().isPrivate()).isFalse();
// Make the change private again.
r = pushTo("refs/for/master%private");
r.assertOkStatus();
+ r.assertMessage(" [PRIVATE]");
assertThat(r.getChange().change().isPrivate()).isTrue();
// Can't use --private and --remove-private together.
@@ -518,30 +539,35 @@
// Push a work-in-progress change.
PushOneCommit.Result r = pushTo("refs/for/master%wip");
r.assertOkStatus();
+ r.assertMessage(" [WIP]");
assertThat(r.getChange().change().isWorkInProgress()).isTrue();
assertUploadTag(r.getChange(), ChangeMessagesUtil.TAG_UPLOADED_WIP_PATCH_SET);
// Pushing a new patch set without --wip doesn't remove the wip flag from the change.
r = amendChange(r.getChangeId(), "refs/for/master");
r.assertOkStatus();
+ r.assertMessage(" [WIP]");
assertThat(r.getChange().change().isWorkInProgress()).isTrue();
assertUploadTag(r.getChange(), ChangeMessagesUtil.TAG_UPLOADED_WIP_PATCH_SET);
// Remove the wip flag from the change.
r = amendChange(r.getChangeId(), "refs/for/master%ready");
r.assertOkStatus();
+ r.assertNotMessage(" [WIP]");
assertThat(r.getChange().change().isWorkInProgress()).isFalse();
assertUploadTag(r.getChange(), ChangeMessagesUtil.TAG_UPLOADED_PATCH_SET);
// Normal push: wip flag is not added back.
r = amendChange(r.getChangeId(), "refs/for/master");
r.assertOkStatus();
+ r.assertNotMessage(" [WIP]");
assertThat(r.getChange().change().isWorkInProgress()).isFalse();
assertUploadTag(r.getChange(), ChangeMessagesUtil.TAG_UPLOADED_PATCH_SET);
// Make the change work-in-progress again.
r = amendChange(r.getChangeId(), "refs/for/master%wip");
r.assertOkStatus();
+ r.assertMessage(" [WIP]");
assertThat(r.getChange().change().isWorkInProgress()).isTrue();
assertUploadTag(r.getChange(), ChangeMessagesUtil.TAG_UPLOADED_WIP_PATCH_SET);
@@ -635,6 +661,9 @@
r.assertMessage(
"Updated Changes:\n "
+ canonicalWebUrl.get()
+ + "#/c/"
+ + project.get()
+ + "/+/"
+ r.getChange().getId()
+ " "
+ editInfo.commit.subject
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
index 0517675..8c50b90 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
@@ -61,6 +61,7 @@
@NoHttpd
public class StandaloneNoteDbMigrationIT extends StandaloneSiteTest {
private StoredConfig gerritConfig;
+ private StoredConfig noteDbConfig;
private Project.NameKey project;
private Change.Id changeId;
@@ -69,10 +70,14 @@
public void setUp() throws Exception {
assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
+ // Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config.
+ noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect());
}
@Test
public void rebuildOneChangeTrialMode() throws Exception {
+ assertNoAutoMigrateConfig(gerritConfig);
+ assertNoAutoMigrateConfig(noteDbConfig);
assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
setUpOneChange();
@@ -101,6 +106,8 @@
@Test
public void migrateOneChange() throws Exception {
+ assertNoAutoMigrateConfig(gerritConfig);
+ assertNoAutoMigrateConfig(noteDbConfig);
assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
setUpOneChange();
@@ -128,6 +135,8 @@
assertThat(db.changes().get(id2)).isNull();
}
}
+ assertNoAutoMigrateConfig(gerritConfig);
+ assertAutoMigrateConfig(noteDbConfig, false);
}
@Test
@@ -151,6 +160,40 @@
@Test
public void onlineMigrationViaDaemon() throws Exception {
+ assertNoAutoMigrateConfig(gerritConfig);
+ assertNoAutoMigrateConfig(noteDbConfig);
+
+ testOnlineMigration(u -> startServer(u.module(), "--migrate-to-note-db", "true"));
+
+ assertNoAutoMigrateConfig(gerritConfig);
+ assertAutoMigrateConfig(noteDbConfig, false);
+ }
+
+ @Test
+ public void onlineMigrationViaConfig() throws Exception {
+ assertNoAutoMigrateConfig(gerritConfig);
+ assertNoAutoMigrateConfig(noteDbConfig);
+
+ testOnlineMigration(
+ u -> {
+ gerritConfig.setBoolean("noteDb", "changes", "autoMigrate", true);
+ gerritConfig.save();
+ return startServer(u.module());
+ });
+
+ // Auto-migration is turned off in notedb.config, which takes precedence, but is still on in
+ // gerrit.config. This means Puppet can continue overwriting gerrit.config without turning
+ // auto-migration back on.
+ assertAutoMigrateConfig(gerritConfig, true);
+ assertAutoMigrateConfig(noteDbConfig, false);
+ }
+
+ @FunctionalInterface
+ private interface StartServerWithMigration {
+ ServerContext start(IndexUpgradeController u) throws Exception;
+ }
+
+ private void testOnlineMigration(StartServerWithMigration start) throws Exception {
assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
int prevVersion = ChangeSchemaDefinitions.INSTANCE.getPrevious().getVersion();
int currVersion = ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion();
@@ -166,7 +209,7 @@
setOnlineUpgradeConfig(true);
IndexUpgradeController u = new IndexUpgradeController(1);
- try (ServerContext ctx = startServer(u.module(), "--migrate-to-note-db", "true")) {
+ try (ServerContext ctx = start.start(u)) {
ChangeIndexCollection indexes = ctx.getInjector().getInstance(ChangeIndexCollection.class);
assertThat(indexes.getSearchIndex().getSchema().getVersion()).isEqualTo(prevVersion);
@@ -199,8 +242,8 @@
}
private void assertNotesMigrationState(NotesMigrationState expected) throws Exception {
- gerritConfig.load();
- assertThat(NotesMigrationState.forConfig(gerritConfig)).hasValue(expected);
+ noteDbConfig.load();
+ assertThat(NotesMigrationState.forConfig(noteDbConfig)).hasValue(expected);
}
private ReviewDb openUnderlyingReviewDb(ServerContext ctx) throws Exception {
@@ -209,6 +252,17 @@
.open();
}
+ private static void assertNoAutoMigrateConfig(StoredConfig cfg) throws Exception {
+ cfg.load();
+ assertThat(cfg.getString("noteDb", "changes", "autoMigrate")).isNull();
+ }
+
+ private static void assertAutoMigrateConfig(StoredConfig cfg, boolean expected) throws Exception {
+ cfg.load();
+ assertThat(cfg.getString("noteDb", "changes", "autoMigrate")).isNotNull();
+ assertThat(cfg.getBoolean("noteDb", "changes", "autoMigrate", false)).isEqualTo(expected);
+ }
+
private void setOnlineUpgradeConfig(boolean enable) throws Exception {
gerritConfig.load();
gerritConfig.setBoolean("index", null, "onlineUpgrade", enable);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
index ff2b2e1..dd0c7f0 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -14,6 +14,7 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GitUtil;
@@ -26,7 +27,11 @@
import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ProjectApi;
+import com.google.gerrit.extensions.client.ChangeStatus;
+import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -87,6 +92,66 @@
}
@Test
+ public void createAccessChange() throws Exception {
+ // User can see the branch
+ setApiUser(user);
+ gApi.projects().name(newProjectName).branch("refs/heads/master").get();
+
+ ProjectAccessInput accessInput = newProjectAccessInput();
+
+ AccessSectionInfo accessSection = newAccessSectionInfo();
+
+ // Deny read to registered users.
+ PermissionInfo read = newPermissionInfo();
+ PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false);
+ read.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri);
+ read.exclusive = true;
+ accessSection.permissions.put(Permission.READ, read);
+ accessInput.add.put(REFS_HEADS, accessSection);
+
+ setApiUser(user);
+ ChangeInfo out = pApi.accessChange(accessInput);
+
+ assertThat(out.project).isEqualTo(newProjectName);
+ assertThat(out.branch).isEqualTo(RefNames.REFS_CONFIG);
+ assertThat(out.status).isEqualTo(ChangeStatus.NEW);
+ assertThat(out.submitted).isNull();
+
+ setApiUser(admin);
+
+ ReviewInput reviewIn = new ReviewInput();
+ reviewIn.label("Code-Review", (short) 2);
+ gApi.changes().id(out._number).current().review(reviewIn);
+ gApi.changes().id(out._number).current().submit();
+
+ // check that the change took effect.
+ setApiUser(user);
+ try {
+ BranchInfo info = gApi.projects().name(newProjectName).branch("refs/heads/master").get();
+ fail("wanted failure, got " + newGson().toJson(info));
+ } catch (ResourceNotFoundException e) {
+ // OK.
+ }
+
+ // Restore.
+ accessInput.add.clear();
+ accessInput.remove.put(REFS_HEADS, accessSection);
+ setApiUser(user);
+
+ pApi.accessChange(accessInput);
+
+ setApiUser(admin);
+ out = pApi.accessChange(accessInput);
+
+ gApi.changes().id(out._number).current().review(reviewIn);
+ gApi.changes().id(out._number).current().submit();
+
+ // Now it works again.
+ setApiUser(user);
+ gApi.projects().name(newProjectName).branch("refs/heads/master").get();
+ }
+
+ @Test
public void removePermission() throws Exception {
// Add initial permission set
ProjectAccessInput accessInput = newProjectAccessInput();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index b3a53b0..06639fe 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -47,6 +47,7 @@
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.update.BatchUpdate;
@@ -76,6 +77,8 @@
public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Inject private ChangeControl.GenericFactory changeControlFactory;
+ @Inject private ChangeNotes.Factory changeNotesFactory;
+
@Inject private Provider<ConsistencyChecker> checkerProvider;
@Inject private IdentifiedUser.GenericFactory userFactory;
@@ -118,17 +121,17 @@
@Test
public void validMergedChange() throws Exception {
- ChangeControl ctl = mergeChange(incrementPatchSet(insertChange()));
- assertNoProblems(ctl, null);
+ ChangeNotes notes = mergeChange(incrementPatchSet(insertChange()));
+ assertNoProblems(notes, null);
}
@Test
public void missingOwner() throws Exception {
TestAccount owner = accountCreator.create("missing");
- ChangeControl ctl = insertChange(owner);
+ ChangeNotes notes = insertChange(owner);
accountsUpdate.create().deleteByKey(owner.getId());
- assertProblems(ctl, null, problem("Missing change owner: " + owner.getId()));
+ assertProblems(notes, null, problem("Missing change owner: " + owner.getId()));
}
@Test
@@ -136,11 +139,14 @@
// NoteDb can't have a change without a repo.
assumeNoteDbDisabled();
- ChangeControl ctl = insertChange();
- Project.NameKey name = ctl.getProject().getNameKey();
+ ChangeNotes notes = insertChange();
+ Project.NameKey name = notes.getProjectName();
+ // Create control before deleting repo to avoid NoSuchProjectException
+ ChangeControl ctl = controlForNotes(notes);
((InMemoryRepositoryManager) repoManager).deleteRepository(name);
- assertProblems(ctl, null, problem("Destination repository not found: " + name));
+ assertThat(checker.check(ctl, null).problems())
+ .containsExactly(problem("Destination repository not found: " + name));
}
@Test
@@ -149,16 +155,16 @@
// create an invalid patch set.
assumeNoteDbDisabled();
- ChangeControl ctl = insertChange();
+ ChangeNotes notes = insertChange();
PatchSet ps =
newPatchSet(
- ctl.getChange().currentPatchSetId(),
+ notes.getChange().currentPatchSetId(),
"fooooooooooooooooooooooooooooooooooooooo",
adminId);
db.patchSets().update(singleton(ps));
assertProblems(
- ctl,
+ notes,
null,
problem("Invalid revision on patch set 1: fooooooooooooooooooooooooooooooooooooooo"));
}
@@ -169,11 +175,11 @@
@Test
public void patchSetObjectAndRefMissing() throws Exception {
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
- ChangeControl ctl = insertChange();
- PatchSet ps = insertMissingPatchSet(ctl, rev);
- ctl = reload(ctl);
+ ChangeNotes notes = insertChange();
+ PatchSet ps = insertMissingPatchSet(notes, rev);
+ notes = reload(notes);
assertProblems(
- ctl,
+ notes,
null,
problem("Ref missing: " + ps.getId().toRefName()),
problem("Object missing: patch set 2: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
@@ -182,13 +188,13 @@
@Test
public void patchSetObjectAndRefMissingWithFix() throws Exception {
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
- ChangeControl ctl = insertChange();
- PatchSet ps = insertMissingPatchSet(ctl, rev);
- ctl = reload(ctl);
+ ChangeNotes notes = insertChange();
+ PatchSet ps = insertMissingPatchSet(notes, rev);
+ notes = reload(notes);
String refName = ps.getId().toRefName();
assertProblems(
- ctl,
+ notes,
new FixInput(),
problem("Ref missing: " + refName),
problem("Object missing: patch set 2: " + rev));
@@ -196,83 +202,82 @@
@Test
public void patchSetRefMissing() throws Exception {
- ChangeControl ctl = insertChange();
+ ChangeNotes notes = insertChange();
testRepo.update(
- "refs/other/foo",
- ObjectId.fromString(psUtil.current(db, ctl.getNotes()).getRevision().get()));
- String refName = ctl.getChange().currentPatchSetId().toRefName();
+ "refs/other/foo", ObjectId.fromString(psUtil.current(db, notes).getRevision().get()));
+ String refName = notes.getChange().currentPatchSetId().toRefName();
deleteRef(refName);
- assertProblems(ctl, null, problem("Ref missing: " + refName));
+ assertProblems(notes, null, problem("Ref missing: " + refName));
}
@Test
public void patchSetRefMissingWithFix() throws Exception {
- ChangeControl ctl = insertChange();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String rev = psUtil.current(db, notes).getRevision().get();
testRepo.update("refs/other/foo", ObjectId.fromString(rev));
- String refName = ctl.getChange().currentPatchSetId().toRefName();
+ String refName = notes.getChange().currentPatchSetId().toRefName();
deleteRef(refName);
assertProblems(
- ctl, new FixInput(), problem("Ref missing: " + refName, FIXED, "Repaired patch set ref"));
+ notes, new FixInput(), problem("Ref missing: " + refName, FIXED, "Repaired patch set ref"));
assertThat(testRepo.getRepository().exactRef(refName).getObjectId().name()).isEqualTo(rev);
}
@Test
public void patchSetObjectAndRefMissingWithDeletingPatchSet() throws Exception {
- ChangeControl ctl = insertChange();
- PatchSet ps1 = psUtil.current(db, ctl.getNotes());
+ ChangeNotes notes = insertChange();
+ PatchSet ps1 = psUtil.current(db, notes);
String rev2 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
- PatchSet ps2 = insertMissingPatchSet(ctl, rev2);
- ctl = reload(ctl);
+ PatchSet ps2 = insertMissingPatchSet(notes, rev2);
+ notes = reload(notes);
FixInput fix = new FixInput();
fix.deletePatchSetIfCommitMissing = true;
assertProblems(
- ctl,
+ notes,
fix,
problem("Ref missing: " + ps2.getId().toRefName()),
problem("Object missing: patch set 2: " + rev2, FIXED, "Deleted patch set"));
- ctl = reload(ctl);
- assertThat(ctl.getChange().currentPatchSetId().get()).isEqualTo(1);
- assertThat(psUtil.get(db, ctl.getNotes(), ps1.getId())).isNotNull();
- assertThat(psUtil.get(db, ctl.getNotes(), ps2.getId())).isNull();
+ notes = reload(notes);
+ assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(1);
+ assertThat(psUtil.get(db, notes, ps1.getId())).isNotNull();
+ assertThat(psUtil.get(db, notes, ps2.getId())).isNull();
}
@Test
public void patchSetMultipleObjectsMissingWithDeletingPatchSets() throws Exception {
- ChangeControl ctl = insertChange();
- PatchSet ps1 = psUtil.current(db, ctl.getNotes());
+ ChangeNotes notes = insertChange();
+ PatchSet ps1 = psUtil.current(db, notes);
String rev2 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
- PatchSet ps2 = insertMissingPatchSet(ctl, rev2);
+ PatchSet ps2 = insertMissingPatchSet(notes, rev2);
- ctl = incrementPatchSet(reload(ctl));
- PatchSet ps3 = psUtil.current(db, ctl.getNotes());
+ notes = incrementPatchSet(reload(notes));
+ PatchSet ps3 = psUtil.current(db, notes);
String rev4 = "c0ffeeeec0ffeeeec0ffeeeec0ffeeeec0ffeeee";
- PatchSet ps4 = insertMissingPatchSet(ctl, rev4);
- ctl = reload(ctl);
+ PatchSet ps4 = insertMissingPatchSet(notes, rev4);
+ notes = reload(notes);
FixInput fix = new FixInput();
fix.deletePatchSetIfCommitMissing = true;
assertProblems(
- ctl,
+ notes,
fix,
problem("Ref missing: " + ps2.getId().toRefName()),
problem("Object missing: patch set 2: " + rev2, FIXED, "Deleted patch set"),
problem("Ref missing: " + ps4.getId().toRefName()),
problem("Object missing: patch set 4: " + rev4, FIXED, "Deleted patch set"));
- ctl = reload(ctl);
- assertThat(ctl.getChange().currentPatchSetId().get()).isEqualTo(3);
- assertThat(psUtil.get(db, ctl.getNotes(), ps1.getId())).isNotNull();
- assertThat(psUtil.get(db, ctl.getNotes(), ps2.getId())).isNull();
- assertThat(psUtil.get(db, ctl.getNotes(), ps3.getId())).isNotNull();
- assertThat(psUtil.get(db, ctl.getNotes(), ps4.getId())).isNull();
+ notes = reload(notes);
+ assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(3);
+ assertThat(psUtil.get(db, notes, ps1.getId())).isNotNull();
+ assertThat(psUtil.get(db, notes, ps2.getId())).isNull();
+ assertThat(psUtil.get(db, notes, ps3.getId())).isNotNull();
+ assertThat(psUtil.get(db, notes, ps4.getId())).isNull();
}
@Test
@@ -309,13 +314,12 @@
+ rev
+ "\n");
indexer.index(db, c.getProject(), c.getId());
- IdentifiedUser user = userFactory.create(admin.getId());
- ChangeControl ctl = changeControlFactory.controlFor(db, c.getProject(), c.getId(), user);
+ ChangeNotes notes = changeNotesFactory.create(db, c.getProject(), c.getId());
FixInput fix = new FixInput();
fix.deletePatchSetIfCommitMissing = true;
assertProblems(
- ctl,
+ notes,
fix,
problem("Ref missing: " + ps.getId().toRefName()),
problem(
@@ -323,9 +327,9 @@
FIX_FAILED,
"Cannot delete patch set; no patch sets would remain"));
- ctl = reload(ctl);
- assertThat(ctl.getChange().currentPatchSetId().get()).isEqualTo(1);
- assertThat(psUtil.current(db, ctl.getNotes())).isNotNull();
+ notes = reload(notes);
+ assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(1);
+ assertThat(psUtil.current(db, notes)).isNotNull();
}
@Test
@@ -333,25 +337,25 @@
// NoteDb can't create a change without a patch set.
assumeNoteDbDisabled();
- ChangeControl ctl = insertChange();
- db.patchSets().deleteKeys(singleton(ctl.getChange().currentPatchSetId()));
- assertProblems(ctl, null, problem("Current patch set 1 not found"));
+ ChangeNotes notes = insertChange();
+ db.patchSets().deleteKeys(singleton(notes.getChange().currentPatchSetId()));
+ assertProblems(notes, null, problem("Current patch set 1 not found"));
}
@Test
public void duplicatePatchSetRevisions() throws Exception {
- ChangeControl ctl = insertChange();
- PatchSet ps1 = psUtil.current(db, ctl.getNotes());
+ ChangeNotes notes = insertChange();
+ PatchSet ps1 = psUtil.current(db, notes);
String rev = ps1.getRevision().get();
- ctl = incrementPatchSet(ctl, testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
+ notes = incrementPatchSet(notes, testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
- assertProblems(ctl, null, problem("Multiple patch sets pointing to " + rev + ": [1, 2]"));
+ assertProblems(notes, null, problem("Multiple patch sets pointing to " + rev + ": [1, 2]"));
}
@Test
public void missingDestRef() throws Exception {
- ChangeControl ctl = insertChange();
+ ChangeNotes notes = insertChange();
String ref = "refs/heads/master";
// Detach head so we're allowed to delete ref.
@@ -360,16 +364,16 @@
ru.setForceUpdate(true);
assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
- assertProblems(ctl, null, problem("Destination ref not found (may be new branch): " + ref));
+ assertProblems(notes, null, problem("Destination ref not found (may be new branch): " + ref));
}
@Test
public void mergedChangeIsNotMerged() throws Exception {
- ChangeControl ctl = insertChange();
+ ChangeNotes notes = insertChange();
try (BatchUpdate bu = newUpdate(adminId)) {
bu.addOp(
- ctl.getId(),
+ notes.getChangeId(),
new BatchUpdateOp() {
@Override
public boolean updateChange(ChangeContext ctx) throws OrmException {
@@ -380,12 +384,12 @@
});
bu.execute();
}
- ctl = reload(ctl);
+ notes = reload(notes);
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
- ObjectId tip = getDestRef(ctl);
+ String rev = psUtil.current(db, notes).getRevision().get();
+ ObjectId tip = getDestRef(notes);
assertProblems(
- ctl,
+ notes,
null,
problem(
"Patch set 1 ("
@@ -398,14 +402,14 @@
@Test
public void newChangeIsMerged() throws Exception {
- ChangeControl ctl = insertChange();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String rev = psUtil.current(db, notes).getRevision().get();
testRepo
- .branch(ctl.getChange().getDest().get())
+ .branch(notes.getChange().getDest().get())
.update(testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
assertProblems(
- ctl,
+ notes,
null,
problem(
"Patch set 1 ("
@@ -418,14 +422,14 @@
@Test
public void newChangeIsMergedWithFix() throws Exception {
- ChangeControl ctl = insertChange();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String rev = psUtil.current(db, notes).getRevision().get();
testRepo
- .branch(ctl.getChange().getDest().get())
+ .branch(notes.getChange().getDest().get())
.update(testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
assertProblems(
- ctl,
+ notes,
new FixInput(),
problem(
"Patch set 1 ("
@@ -437,38 +441,38 @@
FIXED,
"Marked change as merged"));
- ctl = reload(ctl);
- assertThat(ctl.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertNoProblems(ctl, null);
+ notes = reload(notes);
+ assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertNoProblems(notes, null);
}
@Test
public void extensionApiReturnsUpdatedValueAfterFix() throws Exception {
- ChangeControl ctl = insertChange();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String rev = psUtil.current(db, notes).getRevision().get();
testRepo
- .branch(ctl.getChange().getDest().get())
+ .branch(notes.getChange().getDest().get())
.update(testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
- ChangeInfo info = gApi.changes().id(ctl.getId().get()).info();
+ ChangeInfo info = gApi.changes().id(notes.getChangeId().get()).info();
assertThat(info.status).isEqualTo(ChangeStatus.NEW);
- info = gApi.changes().id(ctl.getId().get()).check(new FixInput());
+ info = gApi.changes().id(notes.getChangeId().get()).check(new FixInput());
assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
}
@Test
public void expectedMergedCommitIsLatestPatchSet() throws Exception {
- ChangeControl ctl = insertChange();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String rev = psUtil.current(db, notes).getRevision().get();
testRepo
- .branch(ctl.getChange().getDest().get())
+ .branch(notes.getChange().getDest().get())
.update(testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
FixInput fix = new FixInput();
fix.expectMergedAs = rev;
assertProblems(
- ctl,
+ notes,
fix,
problem(
"Patch set 1 ("
@@ -480,23 +484,23 @@
FIXED,
"Marked change as merged"));
- ctl = reload(ctl);
- assertThat(ctl.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertNoProblems(ctl, null);
+ notes = reload(notes);
+ assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertNoProblems(notes, null);
}
@Test
public void expectedMergedCommitNotMergedIntoDestination() throws Exception {
- ChangeControl ctl = insertChange();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String rev = psUtil.current(db, notes).getRevision().get();
RevCommit commit = testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
- testRepo.branch(ctl.getChange().getDest().get()).update(commit);
+ testRepo.branch(notes.getChange().getDest().get()).update(commit);
FixInput fix = new FixInput();
RevCommit other = testRepo.commit().message(commit.getFullMessage()).create();
fix.expectMergedAs = other.name();
assertProblems(
- ctl,
+ notes,
fix,
problem(
"Expected merged commit "
@@ -509,9 +513,9 @@
@Test
public void createNewPatchSetForExpectedMergeCommitWithNoChangeId() throws Exception {
- ChangeControl ctl = insertChange();
- String dest = ctl.getChange().getDest().get();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String dest = notes.getChange().getDest().get();
+ String rev = psUtil.current(db, notes).getRevision().get();
RevCommit commit = testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
RevCommit mergedAs =
@@ -520,12 +524,12 @@
assertThat(mergedAs.getFooterLines(FooterConstants.CHANGE_ID)).isEmpty();
testRepo.update(dest, mergedAs);
- assertNoProblems(ctl, null);
+ assertNoProblems(notes, null);
FixInput fix = new FixInput();
fix.expectMergedAs = mergedAs.name();
assertProblems(
- ctl,
+ notes,
fix,
problem(
"No patch set found for merged commit " + mergedAs.name(),
@@ -536,20 +540,19 @@
FIXED,
"Inserted as patch set 2"));
- ctl = reload(ctl);
- PatchSet.Id psId2 = new PatchSet.Id(ctl.getId(), 2);
- assertThat(ctl.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(psUtil.get(db, ctl.getNotes(), psId2).getRevision().get())
- .isEqualTo(mergedAs.name());
+ notes = reload(notes);
+ PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
+ assertThat(psUtil.get(db, notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
- assertNoProblems(ctl, null);
+ assertNoProblems(notes, null);
}
@Test
public void createNewPatchSetForExpectedMergeCommitWithChangeId() throws Exception {
- ChangeControl ctl = insertChange();
- String dest = ctl.getChange().getDest().get();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ ChangeNotes notes = insertChange();
+ String dest = notes.getChange().getDest().get();
+ String rev = psUtil.current(db, notes).getRevision().get();
RevCommit commit = testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
RevCommit mergedAs =
@@ -561,20 +564,20 @@
+ "\n"
+ "\n"
+ "Change-Id: "
- + ctl.getChange().getKey().get()
+ + notes.getChange().getKey().get()
+ "\n")
.create();
testRepo.getRevWalk().parseBody(mergedAs);
assertThat(mergedAs.getFooterLines(FooterConstants.CHANGE_ID))
- .containsExactly(ctl.getChange().getKey().get());
+ .containsExactly(notes.getChange().getKey().get());
testRepo.update(dest, mergedAs);
- assertNoProblems(ctl, null);
+ assertNoProblems(notes, null);
FixInput fix = new FixInput();
fix.expectMergedAs = mergedAs.name();
assertProblems(
- ctl,
+ notes,
fix,
problem(
"No patch set found for merged commit " + mergedAs.name(),
@@ -585,30 +588,29 @@
FIXED,
"Inserted as patch set 2"));
- ctl = reload(ctl);
- PatchSet.Id psId2 = new PatchSet.Id(ctl.getId(), 2);
- assertThat(ctl.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(psUtil.get(db, ctl.getNotes(), psId2).getRevision().get())
- .isEqualTo(mergedAs.name());
+ notes = reload(notes);
+ PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
+ assertThat(psUtil.get(db, notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
- assertNoProblems(ctl, null);
+ assertNoProblems(notes, null);
}
@Test
public void expectedMergedCommitIsOldPatchSetOfSameChange() throws Exception {
- ChangeControl ctl = insertChange();
- PatchSet ps1 = psUtil.current(db, ctl.getNotes());
+ ChangeNotes notes = insertChange();
+ PatchSet ps1 = psUtil.current(db, notes);
String rev1 = ps1.getRevision().get();
- ctl = incrementPatchSet(ctl);
- PatchSet ps2 = psUtil.current(db, ctl.getNotes());
+ notes = incrementPatchSet(notes);
+ PatchSet ps2 = psUtil.current(db, notes);
testRepo
- .branch(ctl.getChange().getDest().get())
+ .branch(notes.getChange().getDest().get())
.update(testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev1)));
FixInput fix = new FixInput();
fix.expectMergedAs = rev1;
assertProblems(
- ctl,
+ notes,
fix,
problem("No patch set found for merged commit " + rev1, FIXED, "Marked change as merged"),
problem(
@@ -626,38 +628,37 @@
FIXED,
"Inserted as patch set 3"));
- ctl = reload(ctl);
- PatchSet.Id psId3 = new PatchSet.Id(ctl.getId(), 3);
- assertThat(ctl.getChange().currentPatchSetId()).isEqualTo(psId3);
- assertThat(ctl.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(psUtil.byChangeAsMap(db, ctl.getNotes()).keySet())
- .containsExactly(ps2.getId(), psId3);
- assertThat(psUtil.get(db, ctl.getNotes(), psId3).getRevision().get()).isEqualTo(rev1);
+ notes = reload(notes);
+ PatchSet.Id psId3 = new PatchSet.Id(notes.getChangeId(), 3);
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId3);
+ assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(ps2.getId(), psId3);
+ assertThat(psUtil.get(db, notes, psId3).getRevision().get()).isEqualTo(rev1);
}
@Test
public void expectedMergedCommitIsDanglingPatchSetOlderThanCurrent() throws Exception {
- ChangeControl ctl = insertChange();
- PatchSet ps1 = psUtil.current(db, ctl.getNotes());
+ ChangeNotes notes = insertChange();
+ PatchSet ps1 = psUtil.current(db, notes);
// Create dangling ref so next ID in the database becomes 3.
- PatchSet.Id psId2 = new PatchSet.Id(ctl.getId(), 2);
+ PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
RevCommit commit2 = patchSetCommit(psId2);
String rev2 = commit2.name();
testRepo.branch(psId2.toRefName()).update(commit2);
- ctl = incrementPatchSet(ctl);
- PatchSet ps3 = psUtil.current(db, ctl.getNotes());
+ notes = incrementPatchSet(notes);
+ PatchSet ps3 = psUtil.current(db, notes);
assertThat(ps3.getId().get()).isEqualTo(3);
testRepo
- .branch(ctl.getChange().getDest().get())
+ .branch(notes.getChange().getDest().get())
.update(testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev2)));
FixInput fix = new FixInput();
fix.expectMergedAs = rev2;
assertProblems(
- ctl,
+ notes,
fix,
problem("No patch set found for merged commit " + rev2, FIXED, "Marked change as merged"),
problem(
@@ -675,34 +676,34 @@
FIXED,
"Inserted as patch set 4"));
- ctl = reload(ctl);
- PatchSet.Id psId4 = new PatchSet.Id(ctl.getId(), 4);
- assertThat(ctl.getChange().currentPatchSetId()).isEqualTo(psId4);
- assertThat(ctl.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(psUtil.byChangeAsMap(db, ctl.getNotes()).keySet())
+ notes = reload(notes);
+ PatchSet.Id psId4 = new PatchSet.Id(notes.getChangeId(), 4);
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId4);
+ assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertThat(psUtil.byChangeAsMap(db, notes).keySet())
.containsExactly(ps1.getId(), ps3.getId(), psId4);
- assertThat(psUtil.get(db, ctl.getNotes(), psId4).getRevision().get()).isEqualTo(rev2);
+ assertThat(psUtil.get(db, notes, psId4).getRevision().get()).isEqualTo(rev2);
}
@Test
public void expectedMergedCommitIsDanglingPatchSetNewerThanCurrent() throws Exception {
- ChangeControl ctl = insertChange();
- PatchSet ps1 = psUtil.current(db, ctl.getNotes());
+ ChangeNotes notes = insertChange();
+ PatchSet ps1 = psUtil.current(db, notes);
// Create dangling ref with no patch set.
- PatchSet.Id psId2 = new PatchSet.Id(ctl.getId(), 2);
+ PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
RevCommit commit2 = patchSetCommit(psId2);
String rev2 = commit2.name();
testRepo.branch(psId2.toRefName()).update(commit2);
testRepo
- .branch(ctl.getChange().getDest().get())
+ .branch(notes.getChange().getDest().get())
.update(testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev2)));
FixInput fix = new FixInput();
fix.expectMergedAs = rev2;
assertProblems(
- ctl,
+ notes,
fix,
problem("No patch set found for merged commit " + rev2, FIXED, "Marked change as merged"),
problem(
@@ -713,20 +714,19 @@
FIXED,
"Inserted as patch set 2"));
- ctl = reload(ctl);
- assertThat(ctl.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(ctl.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(psUtil.byChangeAsMap(db, ctl.getNotes()).keySet())
- .containsExactly(ps1.getId(), psId2);
- assertThat(psUtil.get(db, ctl.getNotes(), psId2).getRevision().get()).isEqualTo(rev2);
+ notes = reload(notes);
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
+ assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(ps1.getId(), psId2);
+ assertThat(psUtil.get(db, notes, psId2).getRevision().get()).isEqualTo(rev2);
}
@Test
public void expectedMergedCommitWithMismatchedChangeId() throws Exception {
- ChangeControl ctl = insertChange();
- String dest = ctl.getChange().getDest().get();
+ ChangeNotes notes = insertChange();
+ String dest = notes.getChange().getDest().get();
RevCommit parent = testRepo.branch(dest).commit().message("parent").create();
- String rev = psUtil.current(db, ctl.getNotes()).getRevision().get();
+ String rev = psUtil.current(db, notes).getRevision().get();
RevCommit commit = testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
testRepo.branch(dest).update(commit);
@@ -741,12 +741,12 @@
assertThat(mergedAs.getFooterLines(FooterConstants.CHANGE_ID)).containsExactly(badId);
testRepo.update(dest, mergedAs);
- assertNoProblems(ctl, null);
+ assertNoProblems(notes, null);
FixInput fix = new FixInput();
fix.expectMergedAs = mergedAs.name();
assertProblems(
- ctl,
+ notes,
fix,
problem(
"Expected merged commit "
@@ -754,30 +754,30 @@
+ " has Change-Id: "
+ badId
+ ", but expected "
- + ctl.getChange().getKey().get()));
+ + notes.getChange().getKey().get()));
}
@Test
public void expectedMergedCommitMatchesMultiplePatchSets() throws Exception {
- ChangeControl ctl1 = insertChange();
- PatchSet.Id psId1 = psUtil.current(db, ctl1.getNotes()).getId();
- String dest = ctl1.getChange().getDest().get();
- String rev = psUtil.current(db, ctl1.getNotes()).getRevision().get();
+ ChangeNotes notes1 = insertChange();
+ PatchSet.Id psId1 = psUtil.current(db, notes1).getId();
+ String dest = notes1.getChange().getDest().get();
+ String rev = psUtil.current(db, notes1).getRevision().get();
RevCommit commit = testRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
testRepo.branch(dest).update(commit);
- ChangeControl ctl2 = insertChange();
- ctl2 = incrementPatchSet(ctl2, commit);
- PatchSet.Id psId2 = psUtil.current(db, ctl2.getNotes()).getId();
+ ChangeNotes notes2 = insertChange();
+ notes2 = incrementPatchSet(notes2, commit);
+ PatchSet.Id psId2 = psUtil.current(db, notes2).getId();
- ChangeControl ctl3 = insertChange();
- ctl3 = incrementPatchSet(ctl3, commit);
- PatchSet.Id psId3 = psUtil.current(db, ctl3.getNotes()).getId();
+ ChangeNotes notes3 = insertChange();
+ notes3 = incrementPatchSet(notes3, commit);
+ PatchSet.Id psId3 = psUtil.current(db, notes3).getId();
FixInput fix = new FixInput();
fix.expectMergedAs = commit.name();
assertProblems(
- ctl1,
+ notes1,
fix,
problem(
"Multiple patch sets for expected merged commit "
@@ -795,15 +795,15 @@
return batchUpdateFactory.create(db, project, userFactory.create(owner), TimeUtil.nowTs());
}
- private ChangeControl insertChange() throws Exception {
+ private ChangeNotes insertChange() throws Exception {
return insertChange(admin);
}
- private ChangeControl insertChange(TestAccount owner) throws Exception {
+ private ChangeNotes insertChange(TestAccount owner) throws Exception {
return insertChange(owner, "refs/heads/master");
}
- private ChangeControl insertChange(TestAccount owner, String dest) throws Exception {
+ private ChangeNotes insertChange(TestAccount owner, String dest) throws Exception {
Change.Id id = new Change.Id(sequences.nextChangeId());
ChangeInserter ins;
try (BatchUpdate bu = newUpdate(owner.getId())) {
@@ -817,35 +817,34 @@
.setSendMail(false);
bu.insertChange(ins).execute();
}
- // Return control for admin regardless of owner.
- return changeControlFactory.controlFor(db, ins.getChange(), userFactory.create(adminId));
+ return changeNotesFactory.create(db, project, ins.getChange().getId());
}
- private PatchSet.Id nextPatchSetId(ChangeControl ctl) throws Exception {
- return ChangeUtil.nextPatchSetId(testRepo.getRepository(), ctl.getChange().currentPatchSetId());
+ private PatchSet.Id nextPatchSetId(ChangeNotes notes) throws Exception {
+ return ChangeUtil.nextPatchSetId(
+ testRepo.getRepository(), notes.getChange().currentPatchSetId());
}
- private ChangeControl incrementPatchSet(ChangeControl ctl) throws Exception {
- return incrementPatchSet(ctl, patchSetCommit(nextPatchSetId(ctl)));
+ private ChangeNotes incrementPatchSet(ChangeNotes notes) throws Exception {
+ return incrementPatchSet(notes, patchSetCommit(nextPatchSetId(notes)));
}
- private ChangeControl incrementPatchSet(ChangeControl ctl, RevCommit commit) throws Exception {
+ private ChangeNotes incrementPatchSet(ChangeNotes notes, RevCommit commit) throws Exception {
PatchSetInserter ins;
- try (BatchUpdate bu = newUpdate(ctl.getChange().getOwner())) {
+ try (BatchUpdate bu = newUpdate(notes.getChange().getOwner())) {
ins =
patchSetInserterFactory
- .create(ctl.getNotes(), nextPatchSetId(ctl), commit)
+ .create(notes, nextPatchSetId(notes), commit)
.setValidate(false)
.setFireRevisionCreated(false)
.setNotify(NotifyHandling.NONE);
- bu.addOp(ctl.getId(), ins).execute();
+ bu.addOp(notes.getChangeId(), ins).execute();
}
- return reload(ctl);
+ return reload(notes);
}
- private ChangeControl reload(ChangeControl ctl) throws Exception {
- return changeControlFactory.controlFor(
- db, ctl.getChange().getProject(), ctl.getId(), ctl.getUser());
+ private ChangeNotes reload(ChangeNotes notes) throws Exception {
+ return changeNotesFactory.create(db, notes.getChange().getProject(), notes.getChangeId());
}
private RevCommit patchSetCommit(PatchSet.Id psId) throws Exception {
@@ -853,12 +852,12 @@
return testRepo.parseBody(c);
}
- private PatchSet insertMissingPatchSet(ChangeControl ctl, String rev) throws Exception {
+ private PatchSet insertMissingPatchSet(ChangeNotes notes, String rev) throws Exception {
// Don't use BatchUpdate since we're manually updating the meta ref rather
// than using ChangeUpdate.
String subject = "Subject for missing commit";
- Change c = new Change(ctl.getChange());
- PatchSet.Id psId = nextPatchSetId(ctl);
+ Change c = new Change(notes.getChange());
+ PatchSet.Id psId = nextPatchSetId(notes);
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
PatchSet ps = newPatchSet(psId, rev, adminId);
@@ -913,19 +912,18 @@
.create();
}
- private ObjectId getDestRef(ChangeControl ctl) throws Exception {
- return testRepo.getRepository().exactRef(ctl.getChange().getDest().get()).getObjectId();
+ private ObjectId getDestRef(ChangeNotes notes) throws Exception {
+ return testRepo.getRepository().exactRef(notes.getChange().getDest().get()).getObjectId();
}
- private ChangeControl mergeChange(ChangeControl ctl) throws Exception {
- final ObjectId oldId = getDestRef(ctl);
- final ObjectId newId =
- ObjectId.fromString(psUtil.current(db, ctl.getNotes()).getRevision().get());
- final String dest = ctl.getChange().getDest().get();
+ private ChangeNotes mergeChange(ChangeNotes notes) throws Exception {
+ final ObjectId oldId = getDestRef(notes);
+ final ObjectId newId = ObjectId.fromString(psUtil.current(db, notes).getRevision().get());
+ final String dest = notes.getChange().getDest().get();
try (BatchUpdate bu = newUpdate(adminId)) {
bu.addOp(
- ctl.getId(),
+ notes.getChangeId(),
new BatchUpdateOp() {
@Override
public void updateRepo(RepoContext ctx) throws IOException {
@@ -941,7 +939,7 @@
});
bu.execute();
}
- return reload(ctl);
+ return reload(notes);
}
private static ProblemInfo problem(String message) {
@@ -958,14 +956,22 @@
}
private void assertProblems(
- ChangeControl ctl, @Nullable FixInput fix, ProblemInfo first, ProblemInfo... rest) {
+ ChangeNotes notes, @Nullable FixInput fix, ProblemInfo first, ProblemInfo... rest)
+ throws Exception {
List<ProblemInfo> expected = new ArrayList<>(1 + rest.length);
expected.add(first);
expected.addAll(Arrays.asList(rest));
- assertThat(checker.check(ctl, fix).problems()).containsExactlyElementsIn(expected).inOrder();
+ assertThat(checker.check(controlForNotes(notes), fix).problems())
+ .containsExactlyElementsIn(expected)
+ .inOrder();
}
- private void assertNoProblems(ChangeControl ctl, @Nullable FixInput fix) {
- assertThat(checker.check(ctl, fix).problems()).isEmpty();
+ private void assertNoProblems(ChangeNotes notes, @Nullable FixInput fix) throws Exception {
+ assertThat(checker.check(controlForNotes(notes), fix).problems()).isEmpty();
+ }
+
+ /** @return {@link ChangeControl} for notes and admin regardless of owner. */
+ private ChangeControl controlForNotes(ChangeNotes notes) throws Exception {
+ return changeControlFactory.controlFor(notes, userFactory.create(admin.id));
}
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
index f08a16e..8aa2497 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
@@ -32,6 +32,7 @@
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
@@ -65,6 +66,7 @@
import org.junit.Test;
@Sandboxed
+@UseLocalDisk
@NoHttpd
public class OnlineNoteDbMigrationIT extends AbstractDaemonTest {
private static final String INVALID_STATE = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
@@ -85,12 +87,13 @@
@Inject private Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
@Inject private Sequences sequences;
- private FileBasedConfig gerritConfig;
+ private FileBasedConfig noteDbConfig;
@Before
public void setUp() throws Exception {
assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
- gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
+ // Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config.
+ noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect());
assertNotesMigrationState(REVIEW_DB);
}
@@ -365,27 +368,27 @@
migrate(b -> b.setStopAtStateForTesting(WRITE));
assertNotesMigrationState(WRITE);
- assertThat(NoteDbMigrator.getAutoMigrate(gerritConfig)).isFalse();
+ assertThat(NoteDbMigrator.getAutoMigrate(noteDbConfig)).isFalse();
migrate(b -> b.setAutoMigrate(true).setStopAtStateForTesting(READ_WRITE_NO_SEQUENCE));
assertNotesMigrationState(READ_WRITE_NO_SEQUENCE);
- assertThat(NoteDbMigrator.getAutoMigrate(gerritConfig)).isTrue();
+ assertThat(NoteDbMigrator.getAutoMigrate(noteDbConfig)).isTrue();
migrate(b -> b);
assertNotesMigrationState(NOTE_DB);
- assertThat(NoteDbMigrator.getAutoMigrate(gerritConfig)).isFalse();
+ assertThat(NoteDbMigrator.getAutoMigrate(noteDbConfig)).isFalse();
}
private void assertNotesMigrationState(NotesMigrationState expected) throws Exception {
assertThat(NotesMigrationState.forNotesMigration(notesMigration)).hasValue(expected);
- gerritConfig.load();
- assertThat(NotesMigrationState.forConfig(gerritConfig)).hasValue(expected);
+ noteDbConfig.load();
+ assertThat(NotesMigrationState.forConfig(noteDbConfig)).hasValue(expected);
}
private void setNotesMigrationState(NotesMigrationState state) throws Exception {
- gerritConfig.load();
- state.setConfigValues(gerritConfig);
- gerritConfig.save();
+ noteDbConfig.load();
+ state.setConfigValues(noteDbConfig);
+ noteDbConfig.save();
notesMigration.setFrom(state);
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
index 55849cc..734acb1 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
+import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -36,6 +37,8 @@
ProjectAccessInfo access(ProjectAccessInput p) throws RestApiException;
+ ChangeInfo accessChange(ProjectAccessInput p) throws RestApiException;
+
ConfigInfo config() throws RestApiException;
ConfigInfo config(ConfigInput in) throws RestApiException;
@@ -163,6 +166,11 @@
}
@Override
+ public ChangeInfo accessChange(ProjectAccessInput input) throws RestApiException {
+ throw new NotImplementedException();
+ }
+
+ @Override
public ConfigInfo config() throws RestApiException {
throw new NotImplementedException();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
index 538c7c2..6971b48 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
@@ -29,9 +29,9 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.git.LabelNormalizer;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
@@ -79,7 +79,8 @@
* Apply approval copy settings from prior PatchSets to a new PatchSet.
*
* @param db review database.
- * @param ctl change control for user uploading PatchSet
+ * @param notes change notes for user uploading PatchSet
+ * @param user user uploading PatchSet
* @param ps new PatchSet
* @param rw open walk that can read the patch set commit; null to open the repo on demand.
* @param repoConfig repo config used for change kind detection; null to read from repo on demand.
@@ -87,19 +88,21 @@
*/
public void copyInReviewDb(
ReviewDb db,
- ChangeControl ctl,
+ ChangeNotes notes,
+ CurrentUser user,
PatchSet ps,
@Nullable RevWalk rw,
@Nullable Config repoConfig)
throws OrmException {
- copyInReviewDb(db, ctl, ps, rw, repoConfig, Collections.emptyList());
+ copyInReviewDb(db, notes, user, ps, rw, repoConfig, Collections.emptyList());
}
/**
* Apply approval copy settings from prior PatchSets to a new PatchSet.
*
* @param db review database.
- * @param ctl change control for user uploading PatchSet
+ * @param notes change notes for user uploading PatchSet
+ * @param user user uploading PatchSet
* @param ps new PatchSet
* @param rw open walk that can read the patch set commit; null to open the repo on demand.
* @param repoConfig repo config used for change kind detection; null to read from repo on demand.
@@ -108,52 +111,57 @@
*/
public void copyInReviewDb(
ReviewDb db,
- ChangeControl ctl,
+ ChangeNotes notes,
+ CurrentUser user,
PatchSet ps,
@Nullable RevWalk rw,
@Nullable Config repoConfig,
Iterable<PatchSetApproval> dontCopy)
throws OrmException {
- if (PrimaryStorage.of(ctl.getChange()) == PrimaryStorage.REVIEW_DB) {
- db.patchSetApprovals().insert(getForPatchSet(db, ctl, ps, rw, repoConfig, dontCopy));
+ if (PrimaryStorage.of(notes.getChange()) == PrimaryStorage.REVIEW_DB) {
+ db.patchSetApprovals().insert(getForPatchSet(db, notes, user, ps, rw, repoConfig, dontCopy));
}
}
Iterable<PatchSetApproval> getForPatchSet(
ReviewDb db,
- ChangeControl ctl,
+ ChangeNotes notes,
+ CurrentUser user,
PatchSet.Id psId,
@Nullable RevWalk rw,
@Nullable Config repoConfig)
throws OrmException {
- return getForPatchSet(db, ctl, psId, rw, repoConfig, Collections.<PatchSetApproval>emptyList());
+ return getForPatchSet(
+ db, notes, user, psId, rw, repoConfig, Collections.<PatchSetApproval>emptyList());
}
Iterable<PatchSetApproval> getForPatchSet(
ReviewDb db,
- ChangeControl ctl,
+ ChangeNotes notes,
+ CurrentUser user,
PatchSet.Id psId,
@Nullable RevWalk rw,
@Nullable Config repoConfig,
Iterable<PatchSetApproval> dontCopy)
throws OrmException {
- PatchSet ps = psUtil.get(db, ctl.getNotes(), psId);
+ PatchSet ps = psUtil.get(db, notes, psId);
if (ps == null) {
return Collections.emptyList();
}
- return getForPatchSet(db, ctl, ps, rw, repoConfig, dontCopy);
+ return getForPatchSet(db, notes, user, ps, rw, repoConfig, dontCopy);
}
private Iterable<PatchSetApproval> getForPatchSet(
ReviewDb db,
- ChangeControl ctl,
+ ChangeNotes notes,
+ CurrentUser user,
PatchSet ps,
@Nullable RevWalk rw,
@Nullable Config repoConfig,
Iterable<PatchSetApproval> dontCopy)
throws OrmException {
checkNotNull(ps, "ps should not be null");
- ChangeData cd = changeDataFactory.create(db, ctl);
+ ChangeData cd = changeDataFactory.create(db, notes);
try {
ProjectState project = projectCache.checkedGet(cd.change().getDest().getParentKey());
ListMultimap<PatchSet.Id, PatchSetApproval> all = cd.approvals();
@@ -204,7 +212,7 @@
byUser.put(psa.getLabel(), psa.getAccountId(), copy(psa, ps.getId()));
}
}
- return labelNormalizer.normalize(ctl, byUser.values()).getNormalized();
+ return labelNormalizer.normalize(notes, user, byUser.values()).getNormalized();
} catch (IOException | PermissionBackendException e) {
throw new OrmException(e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
index d858da5..31107c9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -49,7 +49,6 @@
import com.google.gerrit.server.permissions.LabelPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.util.LabelVote;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -385,7 +384,8 @@
public Iterable<PatchSetApproval> byPatchSet(
ReviewDb db,
- ChangeControl ctl,
+ ChangeNotes notes,
+ CurrentUser user,
PatchSet.Id psId,
@Nullable RevWalk rw,
@Nullable Config repoConfig)
@@ -393,12 +393,13 @@
if (!migration.readChanges()) {
return sortApprovals(db.patchSetApprovals().byPatchSet(psId));
}
- return copier.getForPatchSet(db, ctl, psId, rw, repoConfig);
+ return copier.getForPatchSet(db, notes, user, psId, rw, repoConfig);
}
public Iterable<PatchSetApproval> byPatchSetUser(
ReviewDb db,
- ChangeControl ctl,
+ ChangeNotes notes,
+ CurrentUser user,
PatchSet.Id psId,
Account.Id accountId,
@Nullable RevWalk rw,
@@ -407,7 +408,7 @@
if (!migration.readChanges()) {
return sortApprovals(db.patchSetApprovals().byPatchSetUser(psId, accountId));
}
- return filterApprovals(byPatchSet(db, ctl, psId, rw, repoConfig), accountId);
+ return filterApprovals(byPatchSet(db, notes, user, psId, rw, repoConfig), accountId);
}
public PatchSetApproval getSubmitter(ReviewDb db, ChangeNotes notes, PatchSet.Id c) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java
index b13c43b..75dfe6a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeFinder.java
@@ -25,7 +25,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.change.ChangeTriplet;
-import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -59,7 +59,7 @@
private final Cache<Change.Id, String> changeIdProjectCache;
private final Provider<InternalChangeQuery> queryProvider;
private final Provider<ReviewDb> reviewDb;
- private final ChangeControl.GenericFactory changeControlFactory;
+ private final ChangeNotes.Factory changeNotesFactory;
@Inject
ChangeFinder(
@@ -67,24 +67,22 @@
@Named(CACHE_NAME) Cache<Change.Id, String> changeIdProjectCache,
Provider<InternalChangeQuery> queryProvider,
Provider<ReviewDb> reviewDb,
- ChangeControl.GenericFactory changeControlFactory) {
+ ChangeNotes.Factory changeNotesFactory) {
this.indexConfig = indexConfig;
this.changeIdProjectCache = changeIdProjectCache;
this.queryProvider = queryProvider;
this.reviewDb = reviewDb;
- this.changeControlFactory = changeControlFactory;
+ this.changeNotesFactory = changeNotesFactory;
}
/**
* Find changes matching the given identifier.
*
* @param id change identifier, either a numeric ID, a Change-Id, or project~branch~id triplet.
- * @param user user to wrap in controls.
- * @return possibly-empty list of controls for all matching changes, corresponding to the given
- * user; may or may not be visible.
+ * @return possibly-empty list of notes for all matching changes; may or may not be visible.
* @throws OrmException if an error occurred querying the database.
*/
- public List<ChangeControl> find(String id, CurrentUser user) throws OrmException {
+ public List<ChangeNotes> find(String id) throws OrmException {
if (id.isEmpty()) {
return Collections.emptyList();
}
@@ -95,7 +93,7 @@
// Try project~numericChangeId
Integer n = Ints.tryParse(id.substring(z + 1));
if (n != null) {
- return fromProjectNumber(user, id.substring(0, z), n.intValue());
+ return fromProjectNumber(id.substring(0, z), n.intValue());
}
}
@@ -103,7 +101,7 @@
// Try numeric changeId
Integer n = Ints.tryParse(id);
if (n != null) {
- return find(new Change.Id(n), user);
+ return find(new Change.Id(n));
}
}
@@ -115,33 +113,22 @@
Optional<ChangeTriplet> triplet = ChangeTriplet.parse(id, y, z);
if (triplet.isPresent()) {
ChangeTriplet t = triplet.get();
- return asChangeControls(query.byBranchKey(t.branch(), t.id()), user);
+ return asChangeNotes(query.byBranchKey(t.branch(), t.id()));
}
}
// Try isolated Ihash... format ("Change-Id: Ihash").
- return asChangeControls(query.byKeyPrefix(id), user);
+ return asChangeNotes(query.byKeyPrefix(id));
}
- private List<ChangeControl> fromProjectNumber(CurrentUser user, String project, int changeNumber)
+ private List<ChangeNotes> fromProjectNumber(String project, int changeNumber)
throws OrmException {
Change.Id cId = new Change.Id(changeNumber);
try {
return ImmutableList.of(
- changeControlFactory.controlFor(
- reviewDb.get(), Project.NameKey.parse(project), cId, user));
+ changeNotesFactory.createChecked(reviewDb.get(), Project.NameKey.parse(project), cId));
} catch (NoSuchChangeException e) {
return Collections.emptyList();
- } catch (IllegalArgumentException e) {
- String changeNotFound = String.format("change %s not found in ReviewDb", cId);
- String projectNotFound =
- String.format(
- "passed project %s when creating ChangeNotes for %s, but actual project is",
- project, cId);
- if (e.getMessage().equals(changeNotFound) || e.getMessage().startsWith(projectNotFound)) {
- return Collections.emptyList();
- }
- throw e;
} catch (OrmException e) {
// Distinguish between a RepositoryNotFoundException (project argument invalid) and
// other OrmExceptions (failure in the persistence layer).
@@ -152,18 +139,18 @@
}
}
- public ChangeControl findOne(Change.Id id, CurrentUser user) throws OrmException {
- List<ChangeControl> ctls = find(id, user);
- if (ctls.size() != 1) {
+ public ChangeNotes findOne(Change.Id id) throws OrmException {
+ List<ChangeNotes> notes = find(id);
+ if (notes.size() != 1) {
throw new NoSuchChangeException(id);
}
- return ctls.get(0);
+ return notes.get(0);
}
- public List<ChangeControl> find(Change.Id id, CurrentUser user) throws OrmException {
+ public List<ChangeNotes> find(Change.Id id) throws OrmException {
String project = changeIdProjectCache.getIfPresent(id);
if (project != null) {
- return fromProjectNumber(user, project, id.get());
+ return fromProjectNumber(project, id.get());
}
// Use the index to search for changes, but don't return any stored fields,
@@ -173,17 +160,16 @@
if (r.size() == 1) {
changeIdProjectCache.put(id, r.get(0).project().get());
}
- return asChangeControls(r, user);
+ return asChangeNotes(r);
}
- private List<ChangeControl> asChangeControls(List<ChangeData> cds, CurrentUser user)
- throws OrmException {
- List<ChangeControl> ctls = new ArrayList<>(cds.size());
+ private List<ChangeNotes> asChangeNotes(List<ChangeData> cds) throws OrmException {
+ List<ChangeNotes> notes = new ArrayList<>(cds.size());
if (!indexConfig.separateChangeSubIndexes()) {
for (ChangeData cd : cds) {
- checkedAdd(cd, ctls, user);
+ notes.add(cd.notes());
}
- return ctls;
+ return notes;
}
// If an index implementation uses separate non-atomic subindexes, it's possible to temporarily
@@ -195,18 +181,9 @@
Set<Change.Id> seen = Sets.newHashSetWithExpectedSize(cds.size());
for (ChangeData cd : cds) {
if (seen.add(cd.getId())) {
- checkedAdd(cd, ctls, user);
+ notes.add(cd.notes());
}
}
- return ctls;
- }
-
- private static void checkedAdd(ChangeData cd, List<ChangeControl> ctls, CurrentUser user)
- throws OrmException {
- try {
- ctls.add(cd.changeControl(user));
- } catch (NoSuchChangeException e) {
- // Ignore
- }
+ return notes;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index c9b726b..efd26de 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -42,6 +42,10 @@
public static final Ordering<PatchSet> PS_ID_ORDER =
Ordering.from(comparingInt(PatchSet::getPatchSetId));
+ public static String formatChangeUrl(String canonicalWebUrl, Change change) {
+ return canonicalWebUrl + "#/c/" + change.getProject().get() + "/+/" + change.getChangeId();
+ }
+
/** @return a new unique identifier for change message entities. */
public static String messageUuid() {
byte[] buf = new byte[8];
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index a9fe235..6509247 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -31,6 +31,7 @@
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.api.projects.TagApi;
import com.google.gerrit.extensions.api.projects.TagInfo;
+import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
@@ -43,6 +44,7 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ChildProjectsCollection;
import com.google.gerrit.server.project.CommitsCollection;
+import com.google.gerrit.server.project.CreateAccessChange;
import com.google.gerrit.server.project.CreateProject;
import com.google.gerrit.server.project.DeleteBranches;
import com.google.gerrit.server.project.DeleteTags;
@@ -85,6 +87,7 @@
private final TagApiImpl.Factory tagApi;
private final GetAccess getAccess;
private final SetAccess setAccess;
+ private final CreateAccessChange createAccessChange;
private final GetConfig getConfig;
private final PutConfig putConfig;
private final ListBranches listBranches;
@@ -110,6 +113,7 @@
TagApiImpl.Factory tagApiFactory,
GetAccess getAccess,
SetAccess setAccess,
+ CreateAccessChange createAccessChange,
GetConfig getConfig,
PutConfig putConfig,
ListBranches listBranches,
@@ -134,6 +138,7 @@
tagApiFactory,
getAccess,
setAccess,
+ createAccessChange,
getConfig,
putConfig,
listBranches,
@@ -162,6 +167,7 @@
TagApiImpl.Factory tagApiFactory,
GetAccess getAccess,
SetAccess setAccess,
+ CreateAccessChange createAccessChange,
GetConfig getConfig,
PutConfig putConfig,
ListBranches listBranches,
@@ -186,6 +192,7 @@
tagApiFactory,
getAccess,
setAccess,
+ createAccessChange,
getConfig,
putConfig,
listBranches,
@@ -213,6 +220,7 @@
TagApiImpl.Factory tagApiFactory,
GetAccess getAccess,
SetAccess setAccess,
+ CreateAccessChange createAccessChange,
GetConfig getConfig,
PutConfig putConfig,
ListBranches listBranches,
@@ -247,6 +255,7 @@
this.deleteTags = deleteTags;
this.commitsCollection = commitsCollection;
this.commitApi = commitApi;
+ this.createAccessChange = createAccessChange;
}
@Override
@@ -304,6 +313,15 @@
}
@Override
+ public ChangeInfo accessChange(ProjectAccessInput p) throws RestApiException {
+ try {
+ return createAccessChange.apply(checkExists(), p).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot put access right change", e);
+ }
+ }
+
+ @Override
public void description(DescriptionInput in) throws RestApiException {
try {
putDescription.apply(checkExists(), in);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
index cd0f34e..db13c14 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
@@ -35,7 +35,7 @@
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
@@ -138,31 +138,31 @@
* should use the batch instead of abandoning one by one.
*
* <p>It's the caller's responsibility to ensure that all jobs inside the same batch have the
- * matching project from its ChangeControl. Violations will result in a ResourceConflictException.
+ * matching project from its ChangeData. Violations will result in a ResourceConflictException.
*/
public void batchAbandon(
BatchUpdate.Factory updateFactory,
Project.NameKey project,
CurrentUser user,
- Collection<ChangeControl> controls,
+ Collection<ChangeData> changes,
String msgTxt,
NotifyHandling notifyHandling,
ListMultimap<RecipientType, Account.Id> accountsToNotify)
throws RestApiException, UpdateException {
- if (controls.isEmpty()) {
+ if (changes.isEmpty()) {
return;
}
Account account = user.isIdentifiedUser() ? user.asIdentifiedUser().getAccount() : null;
try (BatchUpdate u = updateFactory.create(dbProvider.get(), project, user, TimeUtil.nowTs())) {
- for (ChangeControl control : controls) {
- if (!project.equals(control.getProject().getNameKey())) {
+ for (ChangeData change : changes) {
+ if (!project.equals(change.project())) {
throw new ResourceConflictException(
String.format(
"Project name \"%s\" doesn't match \"%s\"",
- control.getProject().getNameKey().get(), project.get()));
+ change.project().get(), project.get()));
}
u.addOp(
- control.getId(),
+ change.getId(),
abandonOpFactory.create(account, msgTxt, notifyHandling, accountsToNotify));
}
u.execute();
@@ -173,14 +173,14 @@
BatchUpdate.Factory updateFactory,
Project.NameKey project,
CurrentUser user,
- Collection<ChangeControl> controls,
+ Collection<ChangeData> changes,
String msgTxt)
throws RestApiException, UpdateException {
batchAbandon(
updateFactory,
project,
user,
- controls,
+ changes,
msgTxt,
NotifyHandling.ALL,
ImmutableListMultimap.of());
@@ -190,10 +190,10 @@
BatchUpdate.Factory updateFactory,
Project.NameKey project,
CurrentUser user,
- Collection<ChangeControl> controls)
+ Collection<ChangeData> changes)
throws RestApiException, UpdateException {
batchAbandon(
- updateFactory, project, user, controls, "", NotifyHandling.ALL, ImmutableListMultimap.of());
+ updateFactory, project, user, changes, "", NotifyHandling.ALL, ImmutableListMultimap.of());
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
index 58a908d..3239813 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
@@ -20,7 +20,6 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.config.ChangeCleanupConfig;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
@@ -74,24 +73,23 @@
List<ChangeData> changesToAbandon =
queryProvider.get().enforceVisibility(false).query(queryBuilder.parse(query)).entities();
- ImmutableListMultimap.Builder<Project.NameKey, ChangeControl> builder =
+ ImmutableListMultimap.Builder<Project.NameKey, ChangeData> builder =
ImmutableListMultimap.builder();
for (ChangeData cd : changesToAbandon) {
- ChangeControl control = cd.changeControl(internalUser);
- builder.put(control.getProject().getNameKey(), control);
+ builder.put(cd.project(), cd);
}
int count = 0;
- ListMultimap<Project.NameKey, ChangeControl> abandons = builder.build();
+ ListMultimap<Project.NameKey, ChangeData> abandons = builder.build();
String message = cfg.getAbandonMessage();
for (Project.NameKey project : abandons.keySet()) {
- Collection<ChangeControl> changes = getValidChanges(abandons.get(project), query);
+ Collection<ChangeData> changes = getValidChanges(abandons.get(project), query);
try {
abandon.batchAbandon(updateFactory, project, internalUser, changes, message);
count += changes.size();
} catch (Throwable e) {
StringBuilder msg = new StringBuilder("Failed to auto-abandon inactive change(s):");
- for (ChangeControl change : changes) {
+ for (ChangeData change : changes) {
msg.append(" ").append(change.getId().get());
}
msg.append(".");
@@ -104,12 +102,11 @@
}
}
- private Collection<ChangeControl> getValidChanges(
- Collection<ChangeControl> changeControls, String query)
+ private Collection<ChangeData> getValidChanges(Collection<ChangeData> changes, String query)
throws OrmException, QueryParseException {
- Collection<ChangeControl> validChanges = new ArrayList<>();
- for (ChangeControl cc : changeControls) {
- String newQuery = query + " change:" + cc.getId();
+ Collection<ChangeData> validChanges = new ArrayList<>();
+ for (ChangeData cd : changes) {
+ String newQuery = query + " change:" + cd.getId();
List<ChangeData> changesToAbandon =
queryProvider
.get()
@@ -117,12 +114,12 @@
.query(queryBuilder.parse(newQuery))
.entities();
if (!changesToAbandon.isEmpty()) {
- validChanges.add(cc);
+ validChanges.add(cd);
} else {
log.debug(
"Change data with id \"{}\" does not satisfy the query \"{}\""
+ " any more, hence skipping it in clean up",
- cc.getId(),
+ cd.getId(),
query);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 0a44b34..c2b93a6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -714,7 +714,6 @@
private Map<String, LabelWithStatus> initLabels(
ChangeData cd, LabelTypes labelTypes, boolean standard) throws OrmException {
- // Don't use Maps.newTreeMap(Comparator) due to OpenJDK bug 100167.
Map<String, LabelWithStatus> labels = new TreeMap<>(labelTypes.nameComparator());
for (SubmitRecord rec : submitRecords(cd)) {
if (rec.labels == null) {
@@ -1081,11 +1080,16 @@
private Map<String, Short> currentLabels(PermissionBackend.ForChange perm, ChangeData cd)
throws OrmException {
IdentifiedUser user = perm.user().asIdentifiedUser();
- ChangeControl ctl = cd.changeControl().forUser(user);
Map<String, Short> result = new HashMap<>();
for (PatchSetApproval psa :
approvalsUtil.byPatchSetUser(
- db.get(), ctl, cd.change().currentPatchSetId(), user.getAccountId(), null, null)) {
+ db.get(),
+ cd.notes(),
+ user,
+ cd.change().currentPatchSetId(),
+ user.getAccountId(),
+ null,
+ null)) {
result.put(psa.getLabel(), psa.getValue());
}
return result;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
index d967ab8..3556b42 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
@@ -26,10 +26,12 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeFinder;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.QueryChanges;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -48,6 +50,7 @@
private final CreateChange createChange;
private final ChangeResource.Factory changeResourceFactory;
private final PermissionBackend permissionBackend;
+ private final ChangeControl.GenericFactory changeControlFactory;
@Inject
ChangesCollection(
@@ -58,7 +61,8 @@
ChangeFinder changeFinder,
CreateChange createChange,
ChangeResource.Factory changeResourceFactory,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ ChangeControl.GenericFactory changeControlFactory) {
this.db = db;
this.user = user;
this.queryFactory = queryFactory;
@@ -67,6 +71,7 @@
this.createChange = createChange;
this.changeResourceFactory = changeResourceFactory;
this.permissionBackend = permissionBackend;
+ this.changeControlFactory = changeControlFactory;
}
@Override
@@ -82,34 +87,34 @@
@Override
public ChangeResource parse(TopLevelResource root, IdString id)
throws ResourceNotFoundException, OrmException, PermissionBackendException {
- List<ChangeControl> ctls = changeFinder.find(id.encoded(), user.get());
- if (ctls.isEmpty()) {
+ List<ChangeNotes> notes = changeFinder.find(id.encoded());
+ if (notes.isEmpty()) {
throw new ResourceNotFoundException(id);
- } else if (ctls.size() != 1) {
+ } else if (notes.size() != 1) {
throw new ResourceNotFoundException("Multiple changes found for " + id);
}
- ChangeControl ctl = ctls.get(0);
- if (!canRead(ctl)) {
+ ChangeNotes change = notes.get(0);
+ if (!canRead(change)) {
throw new ResourceNotFoundException(id);
}
- return changeResourceFactory.create(ctl);
+ return changeResourceFactory.create(controlFor(change));
}
public ChangeResource parse(Change.Id id)
throws ResourceNotFoundException, OrmException, PermissionBackendException {
- List<ChangeControl> ctls = changeFinder.find(id, user.get());
- if (ctls.isEmpty()) {
+ List<ChangeNotes> notes = changeFinder.find(id);
+ if (notes.isEmpty()) {
throw new ResourceNotFoundException(toIdString(id));
- } else if (ctls.size() != 1) {
+ } else if (notes.size() != 1) {
throw new ResourceNotFoundException("Multiple changes found for " + id);
}
- ChangeControl ctl = ctls.get(0);
- if (!canRead(ctl)) {
+ ChangeNotes change = notes.get(0);
+ if (!canRead(change)) {
throw new ResourceNotFoundException(toIdString(id));
}
- return changeResourceFactory.create(ctl);
+ return changeResourceFactory.create(controlFor(change));
}
private static IdString toIdString(Change.Id id) {
@@ -126,11 +131,16 @@
return createChange;
}
- private boolean canRead(ChangeControl ctl) throws PermissionBackendException {
- return permissionBackend
- .user(user)
- .change(ctl.getNotes())
- .database(db)
- .test(ChangePermission.READ);
+ private boolean canRead(ChangeNotes notes) throws PermissionBackendException {
+ return permissionBackend.user(user).change(notes).database(db).test(ChangePermission.READ);
+ }
+
+ private ChangeControl controlFor(ChangeNotes notes) throws ResourceNotFoundException {
+ try {
+ return changeControlFactory.controlFor(notes, user.get());
+ } catch (NoSuchChangeException e) {
+ throw new ResourceNotFoundException(
+ String.format("Change %s not found", notes.getChangeId()));
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
index 3cdab63..da2f2fd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
@@ -52,11 +52,11 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.CommitsCollection;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectControl;
@@ -194,19 +194,15 @@
ObjectId parentCommit;
List<String> groups;
if (input.baseChange != null) {
- List<ChangeControl> ctls = changeFinder.find(input.baseChange, rsrc.getUser());
- if (ctls.size() != 1) {
+ List<ChangeNotes> notes = changeFinder.find(input.baseChange);
+ if (notes.size() != 1) {
throw new UnprocessableEntityException("Base change not found: " + input.baseChange);
}
- ChangeControl ctl = Iterables.getOnlyElement(ctls);
- if (!permissionBackend
- .user(user)
- .change(ctl.getNotes())
- .database(db)
- .test(ChangePermission.READ)) {
+ ChangeNotes change = Iterables.getOnlyElement(notes);
+ if (!permissionBackend.user(user).change(change).database(db).test(ChangePermission.READ)) {
throw new UnprocessableEntityException("Base change not found: " + input.baseChange);
}
- PatchSet ps = psUtil.current(db.get(), ctl.getNotes());
+ PatchSet ps = psUtil.current(db.get(), change);
parentCommit = ObjectId.fromString(ps.getRevision().get());
groups = ps.getGroups();
} else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java
index c725089..10164ce 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java
@@ -41,7 +41,6 @@
import com.google.gerrit.server.mail.send.DeleteVoteSender;
import com.google.gerrit.server.mail.send.ReplyToChangeSender;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RemoveReviewerControl;
@@ -164,10 +163,9 @@
public boolean updateChange(ChangeContext ctx)
throws OrmException, AuthException, ResourceNotFoundException, IOException,
PermissionBackendException {
- ChangeControl ctl = ctx.getControl();
- change = ctl.getChange();
+ change = ctx.getChange();
PatchSet.Id psId = change.currentPatchSetId();
- ps = psUtil.current(db.get(), ctl.getNotes());
+ ps = psUtil.current(db.get(), ctx.getNotes());
boolean found = false;
LabelTypes labelTypes = projectState.getLabelTypes(ctx.getNotes(), ctx.getUser());
@@ -175,7 +173,8 @@
for (PatchSetApproval a :
approvalsUtil.byPatchSetUser(
ctx.getDb(),
- ctl,
+ ctx.getNotes(),
+ ctx.getUser(),
psId,
account.getId(),
ctx.getRevWalk(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
index 77f4e5c..05f10b5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -49,7 +49,6 @@
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.ssh.NoSshInfo;
import com.google.gerrit.server.update.BatchUpdateOp;
@@ -228,7 +227,6 @@
public boolean updateChange(ChangeContext ctx)
throws ResourceConflictException, OrmException, IOException {
ReviewDb db = ctx.getDb();
- ChangeControl ctl = ctx.getControl();
change = ctx.getChange();
ChangeUpdate update = ctx.getUpdate(psId);
@@ -261,7 +259,7 @@
description);
if (notify != NotifyHandling.NONE) {
- oldReviewers = approvalsUtil.getReviewers(db, ctl.getNotes());
+ oldReviewers = approvalsUtil.getReviewers(db, ctx.getNotes());
}
if (message != null) {
@@ -283,7 +281,12 @@
change.setCurrentPatchSet(patchSetInfo);
if (copyApprovals) {
approvalCopier.copyInReviewDb(
- db, ctl, patchSet, ctx.getRevWalk(), ctx.getRepoView().getConfig());
+ db,
+ ctx.getNotes(),
+ ctx.getUser(),
+ patchSet,
+ ctx.getRevWalk(),
+ ctx.getRepoView().getConfig());
}
if (changeMessage != null) {
cmUtil.addChangeMessage(db, update, changeMessage);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 8c4dd04..ca62719 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -1311,7 +1311,8 @@
for (PatchSetApproval a :
approvalsUtil.byPatchSetUser(
ctx.getDb(),
- ctx.getControl(),
+ ctx.getNotes(),
+ ctx.getUser(),
psId,
user.getAccountId(),
ctx.getRevWalk(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
index c71c050..60ec582 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -111,7 +111,7 @@
perm,
cd,
approvalsUtil.byPatchSetUser(
- db.get(), ctl, psId, new Account.Id(out._accountId), null, null));
+ db.get(), cd.notes(), ctl.getUser(), psId, new Account.Id(out._accountId), null, null));
}
public ReviewerInfo format(
@@ -122,7 +122,6 @@
throws OrmException, PermissionBackendException {
LabelTypes labelTypes = cd.getLabelTypes();
- // Don't use Maps.newTreeMap(Comparator) due to OpenJDK bug 100167.
out.approvals = new TreeMap<>(labelTypes.nameComparator());
for (PatchSetApproval ca : approvals) {
LabelType at = labelTypes.byLabel(ca.getLabelId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Votes.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Votes.java
index 980e6e5..c2631d5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Votes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Votes.java
@@ -84,7 +84,8 @@
Iterable<PatchSetApproval> byPatchSetUser =
approvalsUtil.byPatchSetUser(
db.get(),
- rsrc.getChangeResource().getControl(),
+ rsrc.getChangeResource().getNotes(),
+ rsrc.getChangeResource().getUser(),
rsrc.getChange().currentPatchSetId(),
rsrc.getReviewerUser().getAccountId(),
null,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfig.java
index fdc3b7f..0715f8e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfig.java
@@ -18,10 +18,12 @@
import org.eclipse.jgit.lib.Config;
class GerritConfig extends Config {
+ private final Config gerritConfig;
private final SecureStore secureStore;
- GerritConfig(Config baseConfig, SecureStore secureStore) {
- super(baseConfig);
+ GerritConfig(Config noteDbConfigOverGerritConfig, Config gerritConfig, SecureStore secureStore) {
+ super(noteDbConfigOverGerritConfig);
+ this.gerritConfig = gerritConfig;
this.secureStore = secureStore;
}
@@ -42,4 +44,11 @@
}
return super.getStringList(section, subsection, name);
}
+
+ @Override
+ public String toText() {
+ // Only show the contents of gerrit.config, hiding the implementation detail that some values
+ // may come from secure.config (or another secure store) and notedb.config.
+ return gerritConfig.toText();
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
index 494b63a..1a59c52 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
@@ -14,11 +14,18 @@
package com.google.gerrit.server.config;
+import static java.util.stream.Collectors.joining;
+
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.securestore.SecureStore;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -41,19 +48,46 @@
@Override
public Config get() {
- FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED);
-
- if (!cfg.getFile().exists()) {
+ FileBasedConfig baseConfig = loadConfig(null, site.gerrit_config);
+ if (!baseConfig.getFile().exists()) {
log.info("No " + site.gerrit_config.toAbsolutePath() + "; assuming defaults");
- return new GerritConfig(cfg, secureStore);
}
+ FileBasedConfig noteDbConfigOverBaseConfig = loadConfig(baseConfig, site.notedb_config);
+ checkNoteDbConfig(noteDbConfigOverBaseConfig);
+
+ return new GerritConfig(noteDbConfigOverBaseConfig, baseConfig, secureStore);
+ }
+
+ private static FileBasedConfig loadConfig(@Nullable Config base, Path path) {
+ FileBasedConfig cfg = new FileBasedConfig(base, path.toFile(), FS.DETECTED);
try {
cfg.load();
} catch (IOException | ConfigInvalidException e) {
throw new ProvisionException(e.getMessage(), e);
}
+ return cfg;
+ }
- return new GerritConfig(cfg, secureStore);
+ private static void checkNoteDbConfig(FileBasedConfig noteDbConfig) {
+ List<String> bad = new ArrayList<>();
+ for (String section : noteDbConfig.getSections()) {
+ if (section.equals(NotesMigration.SECTION_NOTE_DB)) {
+ continue;
+ }
+ for (String subsection : noteDbConfig.getSubsections(section)) {
+ noteDbConfig
+ .getNames(section, subsection, false)
+ .forEach(n -> bad.add(section + "." + subsection + "." + n));
+ }
+ noteDbConfig.getNames(section, false).forEach(n -> bad.add(section + "." + n));
+ }
+ if (!bad.isEmpty()) {
+ throw new ProvisionException(
+ "Non-NoteDb config options not allowed in "
+ + noteDbConfig.getFile()
+ + ":\n"
+ + bad.stream().collect(joining("\n")));
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
index c27f5b9..3748bfd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
@@ -53,6 +53,7 @@
public final Path gerrit_config;
public final Path secure_config;
+ public final Path notedb_config;
public final Path ssl_keystore;
public final Path ssh_key;
@@ -100,6 +101,7 @@
gerrit_config = etc_dir.resolve("gerrit.config");
secure_config = etc_dir.resolve("secure.config");
+ notedb_config = etc_dir.resolve("notedb.config");
ssl_keystore = etc_dir.resolve("keystore");
ssh_key = etc_dir.resolve("ssh_host_key");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeReportFormatter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeReportFormatter.java
new file mode 100644
index 0000000..6b1fd85
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeReportFormatter.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2017 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.
+
+package com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.client.Change;
+
+public interface ChangeReportFormatter {
+ public static class Input {
+ private final Change change;
+ private String subject;
+ private Boolean draft;
+ private Boolean edit;
+ private Boolean isPrivate;
+ private Boolean wip;
+
+ public Input(Change change) {
+ this.change = change;
+ }
+
+ public Input setPrivate(boolean isPrivate) {
+ this.isPrivate = isPrivate;
+ return this;
+ }
+
+ public Input setDraft(boolean draft) {
+ this.draft = draft;
+ return this;
+ }
+
+ public Input setEdit(boolean edit) {
+ this.edit = edit;
+ return this;
+ }
+
+ public Input setWorkInProgress(boolean wip) {
+ this.wip = wip;
+ return this;
+ }
+
+ public Input setSubject(String subject) {
+ this.subject = subject;
+ return this;
+ }
+
+ public Change getChange() {
+ return change;
+ }
+
+ public String getSubject() {
+ return subject == null ? change.getSubject() : subject;
+ }
+
+ public boolean isDraft() {
+ return draft == null ? Change.Status.DRAFT == change.getStatus() : draft;
+ }
+
+ public boolean isEdit() {
+ return edit == null ? false : edit;
+ }
+
+ public boolean isPrivate() {
+ return isPrivate == null ? change.isPrivate() : isPrivate;
+ }
+
+ public boolean isWorkInProgress() {
+ return wip == null ? change.isWorkInProgress() : wip;
+ }
+ }
+
+ String newChange(Input input);
+
+ String changeUpdated(Input input);
+
+ String changeClosed(Input input);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
new file mode 100644
index 0000000..297770c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2017 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.
+
+package com.google.gerrit.server.git;
+
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.inject.Inject;
+
+public class DefaultChangeReportFormatter implements ChangeReportFormatter {
+ private final String canonicalWebUrl;
+
+ @Inject
+ DefaultChangeReportFormatter(@CanonicalWebUrl String canonicalWebUrl) {
+ this.canonicalWebUrl = canonicalWebUrl;
+ }
+
+ @Override
+ public String newChange(ChangeReportFormatter.Input input) {
+ return formatChangeUrl(canonicalWebUrl, input);
+ }
+
+ @Override
+ public String changeUpdated(ChangeReportFormatter.Input input) {
+ return formatChangeUrl(canonicalWebUrl, input);
+ }
+
+ @Override
+ public String changeClosed(ChangeReportFormatter.Input input) {
+ return String.format(
+ "change %s closed", ChangeUtil.formatChangeUrl(canonicalWebUrl, input.getChange()));
+ }
+
+ private String formatChangeUrl(String url, Input input) {
+ StringBuilder m =
+ new StringBuilder()
+ .append(" ")
+ .append(ChangeUtil.formatChangeUrl(url, input.getChange()))
+ .append(" ")
+ .append(ChangeUtil.cropSubject(input.getSubject()));
+ if (input.isDraft()) {
+ m.append(" [DRAFT]");
+ }
+ if (input.isEdit()) {
+ m.append(" [EDIT]");
+ }
+ if (input.isPrivate()) {
+ m.append(" [PRIVATE]");
+ }
+ if (input.isWorkInProgress()) {
+ m.append(" [WIP]");
+ }
+ return m.toString();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
index 15a9d74..92514da 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.git;
import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
import org.eclipse.jgit.transport.PostUploadHook;
@@ -26,5 +27,7 @@
factory(MetaDataUpdate.InternalFactory.class);
bind(MetaDataUpdate.Server.class);
DynamicSet.bind(binder(), PostUploadHook.class).to(UploadPackMetricsHook.class);
+ DynamicItem.itemOf(binder(), ChangeReportFormatter.class);
+ DynamicItem.bind(binder(), ChangeReportFormatter.class).to(DefaultChangeReportFormatter.class);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java
index 2eeed24..6a332cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java
@@ -28,16 +28,18 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.LabelPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.io.IOException;
import java.util.Collection;
import java.util.List;
@@ -76,57 +78,59 @@
}
private final Provider<ReviewDb> db;
- private final ChangeControl.GenericFactory changeFactory;
private final IdentifiedUser.GenericFactory userFactory;
private final PermissionBackend permissionBackend;
+ private final ProjectCache projectCache;
@Inject
LabelNormalizer(
Provider<ReviewDb> db,
- ChangeControl.GenericFactory changeFactory,
IdentifiedUser.GenericFactory userFactory,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ ProjectCache projectCache) {
this.db = db;
- this.changeFactory = changeFactory;
this.userFactory = userFactory;
this.permissionBackend = permissionBackend;
+ this.projectCache = projectCache;
}
/**
- * @param change change containing the given approvals.
+ * @param notes change containing the given approvals.
* @param approvals list of approvals.
* @return copies of approvals normalized to the defined ranges for the label type and permissions
* for the user. Approvals for unknown labels are not included in the output, nor are
* approvals where the user has no permissions for that label.
* @throws OrmException
*/
- public Result normalize(Change change, Collection<PatchSetApproval> approvals)
- throws OrmException, PermissionBackendException {
- IdentifiedUser user = userFactory.create(change.getOwner());
- return normalize(changeFactory.controlFor(db.get(), change, user), approvals);
+ public Result normalize(ChangeNotes notes, Collection<PatchSetApproval> approvals)
+ throws OrmException, PermissionBackendException, IOException {
+ IdentifiedUser user = userFactory.create(notes.getChange().getOwner());
+ return normalize(notes, user, approvals);
}
/**
- * @param ctl change control containing the given approvals.
+ * @param notes change notes containing the given approvals.
+ * @param user current user.
* @param approvals list of approvals.
* @return copies of approvals normalized to the defined ranges for the label type and permissions
* for the user. Approvals for unknown labels are not included in the output, nor are
* approvals where the user has no permissions for that label.
*/
- public Result normalize(ChangeControl ctl, Collection<PatchSetApproval> approvals)
- throws PermissionBackendException {
+ public Result normalize(
+ ChangeNotes notes, CurrentUser user, Collection<PatchSetApproval> approvals)
+ throws PermissionBackendException, IOException {
List<PatchSetApproval> unchanged = Lists.newArrayListWithCapacity(approvals.size());
List<PatchSetApproval> updated = Lists.newArrayListWithCapacity(approvals.size());
List<PatchSetApproval> deleted = Lists.newArrayListWithCapacity(approvals.size());
LabelTypes labelTypes =
- ctl.getProjectControl().getProjectState().getLabelTypes(ctl.getNotes(), ctl.getUser());
+ projectCache.checkedGet(notes.getProjectName()).getLabelTypes(notes, user);
for (PatchSetApproval psa : approvals) {
Change.Id changeId = psa.getKey().getParentKey().getParentKey();
checkArgument(
- changeId.equals(ctl.getId()),
+ changeId.equals(notes.getChangeId()),
"Approval %s does not match change %s",
psa.getKey(),
- ctl.getChange().getKey());
+ notes.getChange().getKey());
if (psa.isLegacySubmit()) {
unchanged.add(psa);
continue;
@@ -138,7 +142,7 @@
}
PatchSetApproval copy = copy(psa);
applyTypeFloor(label, copy);
- if (!applyRightFloor(ctl.getNotes(), label, copy)) {
+ if (!applyRightFloor(notes, label, copy)) {
deleted.add(psa);
} else if (copy.getValue() != psa.getValue()) {
updated.add(copy);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
index 1cabc53..8f409ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -39,11 +39,13 @@
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.strategy.CommitMergeStatus;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.server.OrmException;
@@ -355,7 +357,7 @@
PatchSetApproval submitAudit = null;
- for (PatchSetApproval a : safeGetApprovals(ctl, psId)) {
+ for (PatchSetApproval a : safeGetApprovals(ctl.getNotes(), ctl.getUser(), psId)) {
if (a.getValue() <= 0) {
// Negative votes aren't counted.
continue;
@@ -448,9 +450,10 @@
return "Verified".equalsIgnoreCase(id.get());
}
- private Iterable<PatchSetApproval> safeGetApprovals(ChangeControl ctl, PatchSet.Id psId) {
+ private Iterable<PatchSetApproval> safeGetApprovals(
+ ChangeNotes notes, CurrentUser user, PatchSet.Id psId) {
try {
- return approvalsUtil.byPatchSet(db.get(), ctl, psId, null, null);
+ return approvalsUtil.byPatchSet(db.get(), notes, user, psId, null, null);
} catch (OrmException e) {
log.error("Can't read approval records for " + psId, e);
return Collections.emptyList();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 042dad2..b1edb92 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -66,6 +66,7 @@
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicMap.Entry;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -93,13 +94,13 @@
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.SetHashtagsOp;
import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.git.BanCommit;
+import com.google.gerrit.server.git.ChangeReportFormatter;
import com.google.gerrit.server.git.GroupCollector;
import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.MergeOpRepoManager;
@@ -318,7 +319,6 @@
private final Sequences seq;
private final SetHashtagsOp.Factory hashtagsFactory;
private final SshInfo sshInfo;
- private final String canonicalWebUrl;
private final SubmoduleOp.Factory subOpFactory;
private final TagCache tagCache;
private final CreateRefControl createRefControl;
@@ -370,10 +370,10 @@
private Task closeProgress;
private Task commandProgress;
private MessageSender messageSender;
+ private final ChangeReportFormatter changeFormatter;
@Inject
ReceiveCommits(
- @CanonicalWebUrl String canonicalWebUrl,
AccountResolver accountResolver,
AccountsUpdate.Server accountsUpdate,
AllProjectsName allProjectsName,
@@ -406,6 +406,7 @@
SubmoduleOp.Factory subOpFactory,
TagCache tagCache,
CreateRefControl createRefControl,
+ DynamicItem<ChangeReportFormatter> changeFormatterProvider,
@Assisted ProjectControl projectControl,
@Assisted ReceivePack rp,
@Assisted AllRefsWatcher allRefsWatcher,
@@ -416,9 +417,10 @@
this.accountsUpdate = accountsUpdate;
this.allProjectsName = allProjectsName;
this.batchUpdateFactory = batchUpdateFactory;
- this.canonicalWebUrl = canonicalWebUrl;
this.changeInserterFactory = changeInserterFactory;
this.commitValidatorsFactory = commitValidatorsFactory;
+ this.changeFormatter = changeFormatterProvider.get();
+ this.user = projectControl.getUser().asIdentifiedUser();
this.db = db;
this.editUtil = editUtil;
this.hashtagsFactory = hashtagsFactory;
@@ -454,7 +456,6 @@
// Immutable fields derived from constructor arguments.
repo = rp.getRepository();
- user = projectControl.getUser().asIdentifiedUser();
project = projectControl.getProject();
labelTypes = projectControl.getProjectState().getLabelTypes();
permissions = permissionBackend.user(user).project(project.getNameKey());
@@ -604,13 +605,7 @@
addMessage("");
addMessage("New Changes:");
for (CreateRequest c : created) {
- addMessage(
- formatChangeUrl(
- canonicalWebUrl,
- c.change,
- c.change.getSubject(),
- c.change.getStatus() == Change.Status.DRAFT,
- false));
+ addMessage(changeFormatter.newChange(new ChangeReportFormatter.Input(c.change)));
}
addMessage("");
}
@@ -626,6 +621,20 @@
addMessage("");
addMessage("Updated Changes:");
boolean edit = magicBranch != null && magicBranch.edit;
+ Boolean isPrivate = null;
+ Boolean wip = null;
+ if (magicBranch != null) {
+ if (magicBranch.isPrivate) {
+ isPrivate = true;
+ } else if (magicBranch.removePrivate) {
+ isPrivate = false;
+ }
+ if (magicBranch.workInProgress) {
+ wip = true;
+ } else if (magicBranch.ready) {
+ wip = false;
+ }
+ }
for (ReplaceRequest u : updated) {
String subject;
if (edit) {
@@ -639,36 +648,27 @@
} else {
subject = u.info.getSubject();
}
- addMessage(
- formatChangeUrl(
- canonicalWebUrl,
- u.notes.getChange(),
- subject,
- u.replaceOp != null && u.replaceOp.getPatchSet().isDraft(),
- edit));
+
+ if (isPrivate == null) {
+ isPrivate = u.notes.getChange().isPrivate();
+ }
+ if (wip == null) {
+ wip = u.notes.getChange().isWorkInProgress();
+ }
+
+ ChangeReportFormatter.Input input =
+ new ChangeReportFormatter.Input(u.notes.getChange())
+ .setSubject(subject)
+ .setDraft(u.replaceOp != null && u.replaceOp.getPatchSet().isDraft())
+ .setEdit(edit)
+ .setPrivate(isPrivate)
+ .setWorkInProgress(wip);
+ addMessage(changeFormatter.changeUpdated(input));
}
addMessage("");
}
}
- private static String formatChangeUrl(
- String url, Change change, String subject, boolean draft, boolean edit) {
- StringBuilder m =
- new StringBuilder()
- .append(" ")
- .append(url)
- .append(change.getChangeId())
- .append(" ")
- .append(ChangeUtil.cropSubject(subject));
- if (draft) {
- m.append(" [DRAFT]");
- }
- if (edit) {
- m.append(" [EDIT]");
- }
- return m.toString();
- }
-
private void insertChangesAndPatchSets() {
ReceiveCommand magicBranchCmd = magicBranch != null ? magicBranch.cmd : null;
if (magicBranchCmd != null && magicBranchCmd.getResult() != NOT_ATTEMPTED) {
@@ -1678,7 +1678,7 @@
private boolean requestReplace(
ReceiveCommand cmd, boolean checkMergedInto, Change change, RevCommit newCommit) {
if (change.getStatus().isClosed()) {
- reject(cmd, "change " + canonicalWebUrl + change.getId() + " closed");
+ reject(cmd, changeFormatter.changeClosed(new ChangeReportFormatter.Input(change)));
return false;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 3399071..8c67c72 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -311,7 +311,8 @@
approvals);
approvalCopier.copyInReviewDb(
ctx.getDb(),
- ctx.getControl(),
+ ctx.getNotes(),
+ ctx.getUser(),
newPatchSet,
ctx.getRevWalk(),
ctx.getRepoView().getConfig(),
@@ -401,7 +402,8 @@
for (PatchSetApproval a :
approvalsUtil.byPatchSetUser(
ctx.getDb(),
- ctx.getControl(),
+ ctx.getNotes(),
+ ctx.getUser(),
priorPatchSetId,
ctx.getAccountId(),
ctx.getRevWalk(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java
index affa918..1284fca 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java
@@ -358,7 +358,12 @@
Map<PatchSetApproval.Key, PatchSetApproval> byKey = new HashMap<>();
for (PatchSetApproval psa :
args.approvalsUtil.byPatchSet(
- ctx.getDb(), ctx.getControl(), psId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
+ ctx.getDb(),
+ ctx.getNotes(),
+ ctx.getUser(),
+ psId,
+ ctx.getRevWalk(),
+ ctx.getRepoView().getConfig())) {
byKey.put(psa.getKey(), psa);
}
@@ -372,7 +377,7 @@
// was added. So we need to make sure votes are accurate now. This way if
// permissions get modified in the future, historical records stay accurate.
LabelNormalizer.Result normalized =
- args.labelNormalizer.normalize(ctx.getControl(), byKey.values());
+ args.labelNormalizer.normalize(ctx.getNotes(), ctx.getUser(), byKey.values());
update.putApproval(submitter.getLabel(), submitter.getValue());
saveApprovals(normalized, ctx, update, false);
return normalized;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 68bcda4..0f4f376 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -292,7 +292,8 @@
approvalsUtil
.byPatchSetUser(
ctx.getDb(),
- changeControl,
+ changeControl.getNotes(),
+ changeControl.getUser(),
psId,
ctx.getAccountId(),
ctx.getRevWalk(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
index 9c2e39d..2afb4a5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
@@ -70,7 +70,12 @@
Table<Account.Id, String, PatchSetApproval> neg = HashBasedTable.create();
for (PatchSetApproval ca :
args.approvalsUtil.byPatchSet(
- args.db.get(), changeData.changeControl(), patchSet.getId(), null, null)) {
+ args.db.get(),
+ changeData.notes(),
+ changeData.changeControl().getUser(),
+ patchSet.getId(),
+ null,
+ null)) {
LabelType lt = labelTypes.byLabel(ca.getLabelId());
if (lt == null) {
continue;
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 cd1e226..ad74108 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
@@ -172,7 +172,7 @@
// Prepopulate the change exists with proper noteDbState field.
change = newNoteDbOnlyChange(project, changeId);
} else {
- checkArgument(change != null, "change %s not found in ReviewDb", changeId);
+ 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",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
index 7323c2e..61a0cd6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
@@ -320,6 +320,7 @@
}
private final FileBasedConfig gerritConfig;
+ private final FileBasedConfig noteDbConfig;
private final SchemaFactory<ReviewDb> schemaFactory;
private final GitRepositoryManager repoManager;
private final AllProjectsName allProjects;
@@ -374,7 +375,6 @@
this.userFactory = userFactory;
this.globalNotesMigration = globalNotesMigration;
this.primaryStorageMigrator = primaryStorageMigrator;
- this.gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
this.executor = executor;
this.projects = projects;
this.changes = changes;
@@ -384,6 +384,11 @@
this.forceRebuild = forceRebuild;
this.sequenceGap = sequenceGap;
this.autoMigrate = autoMigrate;
+
+ // Stack notedb.config over gerrit.config, in the same way as GerritServerConfigProvider.
+ this.gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
+ this.noteDbConfig =
+ new FileBasedConfig(gerritConfig, sitePaths.notedb_config.toFile(), FS.detect());
}
@Override
@@ -564,9 +569,10 @@
private Optional<NotesMigrationState> loadState() throws IOException {
try {
gerritConfig.load();
- return NotesMigrationState.forConfig(gerritConfig);
+ noteDbConfig.load();
+ return NotesMigrationState.forConfig(noteDbConfig);
} catch (ConfigInvalidException | IllegalArgumentException e) {
- log.warn("error reading NoteDb migration options from " + gerritConfig.getFile(), e);
+ log.warn("error reading NoteDb migration options from " + noteDbConfig.getFile(), e);
return Optional.empty();
}
}
@@ -597,9 +603,9 @@
? "But found this state:\n" + actualOldState.get().toText()
: "But could not parse the current state"));
}
- newState.setConfigValues(gerritConfig);
- additionalUpdates.accept(gerritConfig);
- gerritConfig.save();
+ newState.setConfigValues(noteDbConfig);
+ additionalUpdates.accept(noteDbConfig);
+ noteDbConfig.save();
// Only set in-memory state once it's been persisted to storage.
globalNotesMigration.setFrom(newState);
@@ -610,9 +616,9 @@
private void enableAutoMigrate() throws MigrationException {
try {
- gerritConfig.load();
- setAutoMigrate(gerritConfig, true);
- gerritConfig.save();
+ noteDbConfig.load();
+ setAutoMigrate(noteDbConfig, true);
+ noteDbConfig.save();
} catch (ConfigInvalidException | IOException e) {
throw new MigrationException("Error saving auto-migration config", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index f5e4542..13fc362 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -293,7 +293,8 @@
}
for (PatchSetApproval ap :
- approvalsUtil.byPatchSet(db, this, getChange().currentPatchSetId(), null, null)) {
+ approvalsUtil.byPatchSet(
+ db, getNotes(), getUser(), getChange().currentPatchSetId(), null, null)) {
LabelType type =
getProjectControl()
.getProjectState()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateAccessChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateAccessChange.java
new file mode 100644
index 0000000..84f7e27
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateAccessChange.java
@@ -0,0 +1,149 @@
+// Copyright (C) 2017 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.
+
+package com.google.gerrit.server.project;
+
+import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.common.errors.PermissionDeniedException;
+import com.google.gerrit.extensions.api.access.ProjectAccessInput;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.UpdateException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.util.List;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+@Singleton
+public class CreateAccessChange implements RestModifyView<ProjectResource, ProjectAccessInput> {
+ private final PermissionBackend permissionBackend;
+ private final Sequences seq;
+ private final ChangeInserter.Factory changeInserterFactory;
+ private final BatchUpdate.Factory updateFactory;
+ private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
+ private final Provider<ReviewDb> db;
+ private final SetAccessUtil setAccess;
+ private final ChangeJson.Factory jsonFactory;
+
+ @Inject
+ CreateAccessChange(
+ PermissionBackend permissionBackend,
+ ChangeInserter.Factory changeInserterFactory,
+ BatchUpdate.Factory updateFactory,
+ Sequences seq,
+ Provider<MetaDataUpdate.User> metaDataUpdateFactory,
+ Provider<ReviewDb> db,
+ SetAccessUtil accessUtil,
+ ChangeJson.Factory jsonFactory) {
+ this.permissionBackend = permissionBackend;
+ this.seq = seq;
+ this.changeInserterFactory = changeInserterFactory;
+ this.updateFactory = updateFactory;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.db = db;
+ this.setAccess = accessUtil;
+ this.jsonFactory = jsonFactory;
+ }
+
+ @Override
+ public Response<ChangeInfo> apply(ProjectResource rsrc, ProjectAccessInput input)
+ throws PermissionBackendException, PermissionDeniedException, IOException,
+ ConfigInvalidException, OrmException, InvalidNameException, UpdateException,
+ RestApiException {
+ MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
+ List<AccessSection> removals = setAccess.getAccessSections(input.remove);
+ List<AccessSection> additions = setAccess.getAccessSections(input.add);
+
+ PermissionBackend.ForRef metaRef =
+ permissionBackend.user(rsrc.getUser()).project(rsrc.getNameKey()).ref(RefNames.REFS_CONFIG);
+ try {
+ metaRef.check(RefPermission.READ);
+ } catch (AuthException denied) {
+ throw new PermissionDeniedException(RefNames.REFS_CONFIG + " not visible");
+ }
+ if (!rsrc.getControl().isOwner()) {
+ try {
+ metaRef.check(RefPermission.CREATE_CHANGE);
+ } catch (AuthException denied) {
+ throw new PermissionDeniedException("cannot create change for " + RefNames.REFS_CONFIG);
+ }
+ }
+
+ Project.NameKey newParentProjectName =
+ input.parent == null ? null : new Project.NameKey(input.parent);
+
+ try (MetaDataUpdate md = metaDataUpdateUser.create(rsrc.getNameKey())) {
+ ProjectConfig config = ProjectConfig.read(md);
+
+ setAccess.validateChanges(config, removals, additions);
+ setAccess.applyChanges(config, removals, additions);
+ try {
+ setAccess.setParentName(
+ rsrc.getUser().asIdentifiedUser(),
+ config,
+ rsrc.getNameKey(),
+ newParentProjectName,
+ false);
+ } catch (AuthException e) {
+ throw new IllegalStateException(e);
+ }
+
+ md.setMessage("Review access change");
+ md.setInsertChangeId(true);
+ Change.Id changeId = new Change.Id(seq.nextChangeId());
+ RevCommit commit =
+ config.commitToNewRef(
+ md, new PatchSet.Id(changeId, Change.INITIAL_PATCH_SET_ID).toRefName());
+
+ try (ObjectInserter objInserter = md.getRepository().newObjectInserter();
+ ObjectReader objReader = objInserter.newReader();
+ RevWalk rw = new RevWalk(objReader);
+ BatchUpdate bu =
+ updateFactory.create(db.get(), rsrc.getNameKey(), rsrc.getUser(), TimeUtil.nowTs())) {
+ bu.setRepository(md.getRepository(), rw, objInserter);
+ ChangeInserter ins = changeInserterFactory.create(changeId, commit, RefNames.REFS_CONFIG);
+ ins.setMessage("First patchset").setValidate(false).setUpdateRef(false);
+ bu.insertChange(ins);
+ bu.execute();
+ return Response.created(jsonFactory.noOptions().format(ins.getChange()));
+ }
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
index 11f3805..1f33bc3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
@@ -48,6 +48,7 @@
get(PROJECT_KIND, "access").to(GetAccess.class);
post(PROJECT_KIND, "access").to(SetAccess.class);
+ put(PROJECT_KIND, "access:review").to(CreateAccessChange.class);
get(PROJECT_KIND, "parent").to(GetParent.class);
put(PROJECT_KIND, "parent").to(SetParent.class);
@@ -96,7 +97,6 @@
get(PROJECT_KIND, "config").to(GetConfig.class);
put(PROJECT_KIND, "config").to(PutConfig.class);
-
post(COMMIT_KIND, "cherrypick").to(CherryPickCommit.class);
factory(DeleteRef.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index fb19ba2..3a1c60a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -718,7 +718,8 @@
try {
currentApprovals =
ImmutableList.copyOf(
- approvalsUtil.byPatchSet(db, changeControl(), c.currentPatchSetId(), null, null));
+ approvalsUtil.byPatchSet(
+ db, notes(), changeControl().getUser(), c.currentPatchSetId(), null, null));
} catch (OrmException e) {
if (e.getCause() instanceof NoSuchChangeException) {
currentApprovals = Collections.emptyList();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index c27a096..c274e56 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -189,7 +189,7 @@
+ "patch_set_id INTEGER DEFAULT 0 NOT NULL, "
+ "file_name VARCHAR(4096) DEFAULT '' NOT NULL, "
+ "CONSTRAINT primary_key_account_patch_reviews "
- + "PRIMARY KEY (account_id, change_id, patch_set_id, file_name)"
+ + "PRIMARY KEY (change_id, patch_set_id, account_id, file_name)"
+ ")");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
index be919a1..4dfc41a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.schema;
import com.google.auto.value.AutoValue;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.gerrit.common.Nullable;
@@ -92,11 +94,11 @@
ProjectWatch.builder()
.project(new Project.NameKey(rs.getString(2)))
.filter(rs.getString(3))
- .notifyAbandonedChanges(rs.getBoolean(4))
- .notifyAllComments(rs.getBoolean(5))
- .notifyNewChanges(rs.getBoolean(6))
- .notifyNewPatchSets(rs.getBoolean(7))
- .notifySubmittedChanges(rs.getBoolean(8));
+ .notifyAbandonedChanges(toBoolean(rs.getString(4)))
+ .notifyAllComments(toBoolean(rs.getString(5)))
+ .notifyNewChanges(toBoolean(rs.getString(6)))
+ .notifyNewPatchSets(toBoolean(rs.getString(7)))
+ .notifySubmittedChanges(toBoolean(rs.getString(8)));
imports.put(accountId, b.build());
}
}
@@ -196,4 +198,9 @@
abstract ProjectWatch build();
}
}
+
+ private static boolean toBoolean(String v) {
+ Preconditions.checkState(!Strings.isNullOrEmpty(v));
+ return v.equals("Y");
+ }
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/RepositoryConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/RepositoryConfigTest.java
index 804e2f2..b3faef4 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/config/RepositoryConfigTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/RepositoryConfigTest.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.config;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.client.SubmitType;
@@ -147,7 +148,7 @@
@Test
public void basePathWhenNotConfigured() {
- assertThat((Object) repoCfg.getBasePath(new NameKey("someProject"))).isNull();
+ assertThat(repoCfg.getBasePath(new NameKey("someProject"))).isNull();
}
@Test
@@ -161,7 +162,7 @@
public void basePathForSpecificFilter() {
String basePath = "/someAbsolutePath/someDirectory";
configureBasePath("someProject", basePath);
- assertThat((Object) repoCfg.getBasePath(new NameKey("someOtherProject"))).isNull();
+ assertThat(repoCfg.getBasePath(new NameKey("someOtherProject"))).isNull();
assertThat(repoCfg.getBasePath(new NameKey("someProject")).toString()).isEqualTo(basePath);
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java
index eabccf7..75b00fd 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java
@@ -40,6 +40,7 @@
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.LabelNormalizer.Result;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.RequestContext;
@@ -69,12 +70,14 @@
@Inject private ProjectCache projectCache;
@Inject private SchemaCreator schemaCreator;
@Inject protected ThreadLocalRequestContext requestContext;
+ @Inject private ChangeNotes.Factory changeNotesFactory;
private LifecycleManager lifecycle;
private ReviewDb db;
private Account.Id userId;
private IdentifiedUser user;
private Change change;
+ private ChangeNotes notes;
@Before
public void setUpInjector() throws Exception {
@@ -131,6 +134,7 @@
ps.setSubject("Test change");
change.setCurrentPatchSet(ps);
db.changes().insert(ImmutableList.of(change));
+ notes = changeNotesFactory.createChecked(db, change);
}
@After
@@ -155,7 +159,7 @@
PatchSetApproval cr = psa(userId, "Code-Review", 2);
PatchSetApproval v = psa(userId, "Verified", 1);
assertEquals(
- Result.create(list(v), list(copy(cr, 1)), list()), norm.normalize(change, list(cr, v)));
+ Result.create(list(v), list(copy(cr, 1)), list()), norm.normalize(notes, list(cr, v)));
}
@Test
@@ -169,14 +173,14 @@
PatchSetApproval v = psa(userId, "Verified", 5);
assertEquals(
Result.create(list(), list(copy(cr, 2), copy(v, 1)), list()),
- norm.normalize(change, list(cr, v)));
+ norm.normalize(notes, list(cr, v)));
}
@Test
public void emptyPermissionRangeOmitsResult() throws Exception {
PatchSetApproval cr = psa(userId, "Code-Review", 1);
PatchSetApproval v = psa(userId, "Verified", 1);
- assertEquals(Result.create(list(), list(), list(cr, v)), norm.normalize(change, list(cr, v)));
+ assertEquals(Result.create(list(), list(), list(cr, v)), norm.normalize(notes, list(cr, v)));
}
@Test
@@ -187,7 +191,7 @@
PatchSetApproval cr = psa(userId, "Code-Review", 0);
PatchSetApproval v = psa(userId, "Verified", 0);
- assertEquals(Result.create(list(cr), list(), list(v)), norm.normalize(change, list(cr, v)));
+ assertEquals(Result.create(list(cr), list(), list(v)), norm.normalize(notes, list(cr, v)));
}
private ProjectConfig loadAllProjects() throws Exception {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index 0801447..da61db8 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -14,9 +14,8 @@
package com.google.gerrit.sshd;
-import static java.util.stream.Collectors.toList;
-
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -30,7 +29,6 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
import com.google.gwtorm.server.OrmException;
@@ -39,7 +37,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
public class ChangeArgumentParser {
private final CurrentUser currentUser;
@@ -85,9 +82,8 @@
ProjectControl projectControl,
boolean useIndex)
throws UnloggedFailure, OrmException, PermissionBackendException {
- List<ChangeControl> matched =
- useIndex ? changeFinder.find(id, currentUser) : changeFromNotesFactory(id, currentUser);
- List<ChangeControl> toAdd = new ArrayList<>(changes.size());
+ List<ChangeNotes> matched = useIndex ? changeFinder.find(id) : changeFromNotesFactory(id);
+ List<ChangeNotes> toAdd = new ArrayList<>(changes.size());
boolean canMaintainServer;
try {
permissionBackend.user(currentUser).check(GlobalPermission.MAINTAIN_SERVER);
@@ -95,16 +91,16 @@
} catch (AuthException | PermissionBackendException e) {
canMaintainServer = false;
}
- for (ChangeControl ctl : matched) {
- if (!changes.containsKey(ctl.getId())
- && inProject(projectControl, ctl.getProject())
+ for (ChangeNotes notes : matched) {
+ if (!changes.containsKey(notes.getChangeId())
+ && inProject(projectControl, notes.getProjectName())
&& (canMaintainServer
|| permissionBackend
.user(currentUser)
- .change(ctl.getNotes())
+ .change(notes)
.database(db)
.test(ChangePermission.READ))) {
- toAdd.add(ctl);
+ toAdd.add(notes);
}
}
@@ -113,19 +109,18 @@
} else if (toAdd.size() > 1) {
throw new UnloggedFailure(1, "\"" + id + "\" matches multiple changes");
}
- ChangeControl ctl = toAdd.get(0);
- changes.put(ctl.getId(), changesCollection.parse(ctl));
+ Change.Id cId = toAdd.get(0).getChangeId();
+ ChangeResource changeResource;
+ try {
+ changeResource = changesCollection.parse(cId);
+ } catch (ResourceNotFoundException e) {
+ throw new UnloggedFailure(1, "\"" + id + "\" no such change");
+ }
+ changes.put(cId, changeResource);
}
- private List<ChangeControl> changeFromNotesFactory(String id, CurrentUser currentUser)
- throws OrmException, UnloggedFailure {
- return changeNotesFactory
- .create(db, parseId(id))
- .stream()
- .map(changeNote -> controlForChange(changeNote, currentUser))
- .filter(changeControl -> changeControl.isPresent())
- .map(changeControl -> changeControl.get())
- .collect(toList());
+ private List<ChangeNotes> changeFromNotesFactory(String id) throws OrmException, UnloggedFailure {
+ return changeNotesFactory.create(db, parseId(id));
}
private List<Change.Id> parseId(String id) throws UnloggedFailure {
@@ -136,17 +131,9 @@
}
}
- private Optional<ChangeControl> controlForChange(ChangeNotes change, CurrentUser user) {
- try {
- return Optional.of(changeControlFactory.controlFor(change, user));
- } catch (NoSuchChangeException e) {
- return Optional.empty();
- }
- }
-
- private boolean inProject(ProjectControl projectControl, Project project) {
+ private boolean inProject(ProjectControl projectControl, Project.NameKey project) {
if (projectControl != null) {
- return projectControl.getProject().getNameKey().equals(project.getNameKey());
+ return projectControl.getProject().getNameKey().equals(project);
}
// No --project option, so they want every project.
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java
index 9b02f38..c3613b1 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java
@@ -21,10 +21,8 @@
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeFinder;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.query.change.ChangeData;
@@ -44,7 +42,6 @@
private final ChangeNotes.Factory notesFactory;
private final PatchSetUtil psUtil;
private final ChangeFinder changeFinder;
- private final Provider<CurrentUser> self;
@Inject
PatchSetParser(
@@ -52,14 +49,12 @@
Provider<InternalChangeQuery> queryProvider,
ChangeNotes.Factory notesFactory,
PatchSetUtil psUtil,
- ChangeFinder changeFinder,
- Provider<CurrentUser> self) {
+ ChangeFinder changeFinder) {
this.db = db;
this.queryProvider = queryProvider;
this.notesFactory = notesFactory;
this.psUtil = psUtil;
this.changeFinder = changeFinder;
- this.self = self;
}
public PatchSet parsePatchSet(String token, ProjectControl projectControl, String branch)
@@ -141,8 +136,8 @@
return notesFactory.create(db.get(), projectControl.getProject().getNameKey(), changeId);
}
try {
- ChangeControl ctl = changeFinder.findOne(changeId, self.get());
- return notesFactory.create(db.get(), ctl.getProject().getNameKey(), changeId);
+ ChangeNotes notes = changeFinder.findOne(changeId);
+ return notesFactory.create(db.get(), notes.getProjectName(), changeId);
} catch (NoSuchChangeException e) {
throw error("\"" + changeId + "\" no such change");
}
diff --git a/plugins/replication b/plugins/replication
index 643d635..e145010 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 643d635a4502e2a2df6cb02edade88bad3fd953a
+Subproject commit e145010de7911623c98e6111537ffa4997d1a82e
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index fe99eb4..5e6d0fb 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit fe99eb4399054d3fd67ff2330aa92841d76e53bb
+Subproject commit 5e6d0fb102a2d5486c94a0ea7463efd1508f5ade
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
index bfced55..9371b17 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
@@ -64,7 +64,7 @@
}
th {
border-bottom: 1px solid #eee;
- font-weight: bold;
+ font-family: var(--font-family-bold);
text-align: left;
}
</style>
diff --git a/polygerrit-ui/app/elements/admin/gr-project-access/gr-project-access.html b/polygerrit-ui/app/elements/admin/gr-project-access/gr-project-access.html
index 686aa10..7b23506 100644
--- a/polygerrit-ui/app/elements/admin/gr-project-access/gr-project-access.html
+++ b/polygerrit-ui/app/elements/admin/gr-project-access/gr-project-access.html
@@ -32,6 +32,12 @@
<style include="shared-styles"></style>
<style include="gr-menu-page-styles"></style>
<main>
+ <template is="dom-if" if="[[_inheritsFrom]]">
+ <h3 id="inheritsFrom">Rights Inherit From
+ <a href$="[[_computeParentHref(_inheritsFrom.name)]]" rel="noopener">
+ [[_inheritsFrom.name]]</a>
+ </h3>
+ </template>
<template
is="dom-repeat"
items="{{_sections}}"
@@ -43,12 +49,6 @@
editing="[[_editing]]"
groups="[[_groups]]"></gr-access-section>
</template>
- <template is="dom-if" if="[[_inheritsFrom]]">
- <h3 id="inheritsFrom">Rights Inherit From
- <a href$="[[_computeParentHref(_inheritsFrom.name)]]" rel="noopener">
- [[_inheritsFrom.name]]</a>
- </h3>
- </template>
</main>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
index 70275b3..4ff10cb 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
@@ -39,7 +39,7 @@
background-color: #ebf5fb;
}
:host([needs-review]) {
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
:host([assigned]) {
background-color: #fcfad6;
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
index bec7429..f04b7c6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
@@ -16,8 +16,9 @@
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../change-list/gr-change-list/gr-change-list.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-dashboard-view">
<template>
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
index 7b18b5e..3d98ecb 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
+++ b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
@@ -37,12 +37,15 @@
.pending-add {
font-style: italic;
}
+ .list {
+ @apply --account-list-style;
+ }
</style>
<!--
NOTE(Issue 6419): Nest the inner dom-repeat template in a div rather than
as a direct child of the dom-module's template.
-->
- <div>
+ <div class="list">
<template id="chips" is="dom-repeat" items="[[accounts]]" as="account">
<gr-account-chip
account="[[account]]"
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 3bccdd8..ff77142 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -892,6 +892,11 @@
*/
_send(method, payload, actionEndpoint, revisionAction, cleanupFn,
opt_errorFn) {
+ const handleError = response => {
+ cleanupFn.call(this);
+ this._handleResponseError(response);
+ };
+
return this.fetchIsLatestKnown(this.change, this.$.restAPI)
.then(isLatest => {
if (!isLatest) {
@@ -904,13 +909,17 @@
Gerrit.Nav.navigateToChange(this.change);
},
});
+
+ // Because this is not a network error, call the cleanup function
+ // but not the error handler.
cleanupFn();
+
return Promise.resolve();
}
const patchNum = revisionAction ? this.patchNum : null;
return this.$.restAPI.getChangeURLAndSend(this.changeNum, method,
- patchNum, actionEndpoint, payload, this._handleResponseError,
- this).then(response => {
+ patchNum, actionEndpoint, payload, handleError, this)
+ .then(response => {
cleanupFn.call(this);
return response;
});
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 55cd982..11a2569 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -1084,5 +1084,88 @@
});
});
});
+
+ suite('_send', () => {
+ let cleanup;
+ let payload;
+ let onShowAlert;
+
+ setup(() => {
+ cleanup = sinon.stub();
+ element.changeNum = 42;
+ element.patchNum = 12;
+ payload = {foo: 'bar'};
+
+ onShowAlert = sinon.stub();
+ element.addEventListener('show-alert', onShowAlert);
+ });
+
+ suite('happy path', () => {
+ let sendStub;
+
+ setup(() => {
+ sandbox.stub(element, 'fetchIsLatestKnown')
+ .returns(Promise.resolve(true));
+ sendStub = sandbox.stub(element.$.restAPI, 'getChangeURLAndSend')
+ .returns(Promise.resolve({}));
+ });
+
+ test('change action', () => {
+ return element._send('DELETE', payload, '/endpoint', false, cleanup)
+ .then(() => {
+ assert.isFalse(onShowAlert.called);
+ assert.isTrue(cleanup.calledOnce);
+ assert.isTrue(sendStub.calledWith(42, 'DELETE', null,
+ '/endpoint', payload));
+ });
+ });
+
+ test('revision action', () => {
+ return element._send('DELETE', payload, '/endpoint', true, cleanup)
+ .then(() => {
+ assert.isFalse(onShowAlert.called);
+ assert.isTrue(cleanup.calledOnce);
+ assert.isTrue(sendStub.calledWith(42, 'DELETE', 12, '/endpoint',
+ payload));
+ });
+ });
+ });
+
+ suite('failure modes', () => {
+ test('non-latest', () => {
+ sandbox.stub(element, 'fetchIsLatestKnown')
+ .returns(Promise.resolve(false));
+ const sendStub = sandbox.stub(element.$.restAPI,
+ 'getChangeURLAndSend');
+
+ return element._send('DELETE', payload, '/endpoint', true, cleanup)
+ .then(() => {
+ assert.isTrue(onShowAlert.calledOnce);
+ assert.isTrue(cleanup.calledOnce);
+ assert.isFalse(sendStub.called);
+ });
+ });
+
+ test('send fails', () => {
+ sandbox.stub(element, 'fetchIsLatestKnown')
+ .returns(Promise.resolve(true));
+ const sendStub = sandbox.stub(element.$.restAPI,
+ 'getChangeURLAndSend',
+ (num, method, patchNum, endpoint, payload, onErr) => {
+ onErr();
+ return Promise.resolve(null);
+ });
+ const handleErrorStub = sandbox.stub(element, '_handleResponseError');
+
+ return element._send('DELETE', payload, '/endpoint', true, cleanup)
+ .then(() => {
+ assert.isFalse(onShowAlert.called);
+ assert.isTrue(cleanup.called);
+ assert.isTrue(sendStub.calledOnce);
+ assert.isTrue(handleErrorStub.called);
+ });
+ });
+ });
+ });
});
</script>
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 5a834b7..388a3f7 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
@@ -43,7 +43,7 @@
}
.title {
color: #666;
- font-weight: bold;
+ font-family: var(--font-family-bold);
max-width: 20em;
word-break: break-word;
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 39ae763..5642fc0 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -77,7 +77,7 @@
.header-title {
flex: 1;
font-size: 1.2em;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
gr-change-star {
margin-right: .25em;
@@ -207,6 +207,9 @@
#fileList {
padding: .5em calc(var(--default-horizontal-margin) / 2);
}
+ .scrollable {
+ overflow: auto;
+ }
@media screen and (min-width: 80em) {
.commitMessage {
max-width: var(--commit-message-max-width, 100ch);
@@ -278,9 +281,6 @@
flex: initial;
margin-right: 0;
}
- .scrollable {
- overflow: auto;
- }
/* Change actions are the only thing thant need to remain visible due
to the fact that they may have the currently visible overlay open. */
#mainContent.overlayOpen .hideOnMobileOverlay {
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
index 2b7b0fd..705cf52 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
@@ -36,7 +36,7 @@
}
.file {
border-top: 1px solid #ddd;
- font-weight: bold;
+ font-family: var(--font-family-bold);
margin: 10px 0 3px;
padding: 10px 0 5px;
}
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 a2a64c8..95bfc70 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
@@ -104,7 +104,7 @@
}
.drafts {
color: #C62828;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
.show-hide {
margin-left: .4em;
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index ae43edf..c0e55ef 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -76,7 +76,7 @@
width: 2.5em;
}
.name {
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
.message {
--gr-formatted-text-prose-max-width: 80ch;
@@ -136,7 +136,7 @@
}
gr-account-label {
--gr-account-label-text-style: {
- font-weight: bold;
+ font-family: var(--font-family-bold);
};
}
</style>
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
index 8c02e65..2ebf7c7 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
@@ -68,7 +68,7 @@
}
.status {
color: #666;
- font-weight: bold;
+ font-family: var(--font-family-bold);
margin-left: .25em;
}
.notCurrent {
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index 04a1b16..2f9f78c 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -76,8 +76,10 @@
display: flex;
flex-wrap: wrap;
flex: 1;
- max-height: 12em;
- overflow-y: auto;
+ --account-list-style: {
+ max-height: 12em;
+ overflow-y: auto;
+ }
}
#reviewerConfirmationOverlay {
padding: 1em;
@@ -87,7 +89,7 @@
margin-top: 1em;
}
.groupName {
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
.groupSize {
font-style: italic;
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
index 3f3350e..2318657 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
@@ -48,12 +48,12 @@
text-align: right;
}
.header {
- font-weight: bold;
+ font-family: var(--font-family-bold);
padding-top: 1em;
}
.key {
display: inline-block;
- font-weight: bold;
+ font-family: var(--font-family-bold);
border-radius: 3px;
background-color: #f1f2f3;
padding: .1em .5em;
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index 5523032..09f3029 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -120,7 +120,7 @@
@media screen and (max-width: 50em) {
.bigTitle {
font-size: 14px;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
gr-search-bar,
.browse,
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index 7089b2e..a056cf1 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -47,6 +47,8 @@
// - `leftSide`, optional, Boolean, if a `lineNum` is provided, a value
// of true selects the line from base of the patch range. False by
// default.
+ // - `edit`, optional, Boolean, whether or not the editor view of the
+ // diff should be loaded.
window.Gerrit = window.Gerrit || {};
@@ -211,6 +213,21 @@
},
/**
+ * Gets the link to load a file in an editor.
+ *
+ * @param {{ _number: number, project: string }} change The change object.
+ * @param {string} path The file path.
+ * @param {number=} opt_patchNum
+ * @param {number|string=} opt_basePatchNum The string 'PARENT' can be
+ * used for none.
+ * @return {string}
+ */
+ getEditUrlForDiff(change, path, opt_patchNum, opt_basePatchNum) {
+ return this.getUrlForDiffById(change._number, change.project, path,
+ opt_patchNum, opt_basePatchNum, null, null, true);
+ },
+
+ /**
* @param {number} changeNum
* @param {string} project The name of the project.
* @param {string} path The file path.
@@ -219,10 +236,11 @@
* used for none.
* @param {number=} opt_lineNum
* @param {boolean=} opt_leftSide
+ * @param {boolean=} opt_edit
* @return {string}
*/
getUrlForDiffById(changeNum, project, path, opt_patchNum,
- opt_basePatchNum, opt_lineNum, opt_leftSide) {
+ opt_basePatchNum, opt_lineNum, opt_leftSide, opt_edit) {
if (opt_basePatchNum === PARENT_PATCHNUM) {
opt_basePatchNum = undefined;
}
@@ -237,6 +255,7 @@
basePatchNum: opt_basePatchNum,
lineNum: opt_lineNum,
leftSide: opt_leftSide,
+ edit: opt_edit,
});
},
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index 2de74e1..a2861c6 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -91,7 +91,7 @@
QUERY_OFFSET: '/q/:query,:offset',
// Matches /c/<changeNum>/[<basePatchNum>..][<patchNum>][/].
- CHNAGE_LEGACY: /^\/c\/(\d+)\/?(((\d+|edit)(\.\.(\d+|edit))?))?\/?$/,
+ CHANGE_LEGACY: /^\/c\/(\d+)\/?(((\d+|edit)(\.\.(\d+|edit))?))?\/?$/,
CHANGE_NUMBER_LEGACY: /^\/(\d+)\/?/,
// Matches
@@ -112,6 +112,10 @@
SETTINGS: /^\/settings\/?/,
SETTINGS_LEGACY: /^\/settings\/VE\/(\S+)/,
+
+ // Matches /c/<changeNum>/ /<URL tail>
+ // Catches improperly encoded URLs (context: Issue 7100)
+ IMPROPERLY_ENCODED_PLUS: /^\/c\/(.+)\/\ \/(.+)$/,
};
/**
@@ -514,7 +518,7 @@
this._mapRoute(RoutePattern.CHANGE_OR_DIFF, '_handleChangeOrDiffRoute');
- this._mapRoute(RoutePattern.CHNAGE_LEGACY, '_handleChangeLegacyRoute');
+ this._mapRoute(RoutePattern.CHANGE_LEGACY, '_handleChangeLegacyRoute');
this._mapRoute(RoutePattern.DIFF_LEGACY, '_handleDiffLegacyRoute');
@@ -529,6 +533,9 @@
this._mapRoute(RoutePattern.LOG_IN_OR_OUT, '_handlePassThroughRoute');
+ this._mapRoute(RoutePattern.IMPROPERLY_ENCODED_PLUS,
+ '_handleImproperlyEncodedPlusRoute');
+
// Note: this route should appear last so it only catches URLs unmatched
// by other patterns.
this._mapRoute(RoutePattern.DEFAULT, '_handleDefaultRoute');
@@ -957,6 +964,17 @@
location.reload();
},
+
+ /**
+ * URL may sometimes have /+/ encoded to / /.
+ * Context: Issue 6888, Issue 7100
+ */
+ _handleImproperlyEncodedPlusRoute(ctx) {
+ let hash = this._getHashFromCanonicalPath(ctx.canonicalPath);
+ if (hash.length) { hash = '#' + hash; }
+ this._redirect(`/c/${ctx.params[0]}/+/${ctx.params[1]}${hash}`);
+ },
+
/**
* Catchall route for when no other route is matched.
*/
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
index 831b905..df46a38 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
@@ -154,6 +154,7 @@
'_handleChangeLegacyRoute',
'_handleDiffLegacyRoute',
'_handleGroupMembersRoute',
+ '_handleImproperlyEncodedPlusRoute',
'_handlePassThroughRoute',
'_handleProjectAccessRoute',
'_handleProjectListFilterOffsetRoute',
@@ -433,6 +434,23 @@
404);
});
+ test('_handleImproperlyEncodedPlusRoute', () => {
+ // Regression test for Issue 7100.
+ element._handleImproperlyEncodedPlusRoute(
+ {canonicalPath: '/c/test/%20/42', params: ['test', '42']});
+ assert.isTrue(redirectStub.calledOnce);
+ assert.equal(
+ redirectStub.lastCall.args[0],
+ '/c/test/+/42');
+
+ sandbox.stub(element, '_getHashFromCanonicalPath').returns('foo');
+ element._handleImproperlyEncodedPlusRoute(
+ {canonicalPath: '/c/test/%20/42', params: ['test', '42']});
+ assert.equal(
+ redirectStub.lastCall.args[0],
+ '/c/test/+/42#foo');
+ });
+
suite('_handleRootRoute', () => {
test('closes for closeAfterLogin', () => {
const data = {querystring: 'closeAfterLogin', canonicalPath: ''};
@@ -547,10 +565,10 @@
});
suite('_handleDashboardRoute', () => {
- let reidrectToLoginStub;
+ let redirectToLoginStub;
setup(() => {
- reidrectToLoginStub = sandbox.stub(element, '_redirectToLogin');
+ redirectToLoginStub = sandbox.stub(element, '_redirectToLogin');
});
test('no user specified', () => {
@@ -558,7 +576,7 @@
const result = element._handleDashboardRoute(data);
assert.isNotOk(result);
assert.isFalse(setParamsStub.called);
- assert.isFalse(reidrectToLoginStub.called);
+ assert.isFalse(redirectToLoginStub.called);
assert.isTrue(redirectStub.called);
assert.equal(redirectStub.lastCall.args[0], '/dashboard/self');
});
@@ -570,7 +588,7 @@
const result = element._handleDashboardRoute(data);
assert.isOk(result);
return result.then(() => {
- assert.isTrue(reidrectToLoginStub.calledOnce);
+ assert.isTrue(redirectToLoginStub.calledOnce);
assert.isFalse(redirectStub.called);
assert.isFalse(setParamsStub.called);
});
@@ -583,7 +601,7 @@
const result = element._handleDashboardRoute(data);
assert.isOk(result);
return result.then(() => {
- assert.isFalse(reidrectToLoginStub.called);
+ assert.isFalse(redirectToLoginStub.called);
assert.isFalse(setParamsStub.called);
assert.isTrue(redirectStub.calledOnce);
assert.equal(redirectStub.lastCall.args[0], '/q/owner:foo');
@@ -597,7 +615,7 @@
const result = element._handleDashboardRoute(data);
assert.isOk(result);
return result.then(() => {
- assert.isFalse(reidrectToLoginStub.called);
+ assert.isFalse(redirectToLoginStub.called);
assert.isFalse(redirectStub.called);
assert.isTrue(setParamsStub.calledOnce);
assert.deepEqual(setParamsStub.lastCall.args[0], {
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index 07e306a..bd8e680 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -14,7 +14,7 @@
(function() {
'use strict';
- // Possible static search options for auto complete.
+ // Possible static search options for auto complete, without negations.
const SEARCH_OPERATORS = [
'added:',
'age:',
@@ -56,6 +56,7 @@
'is:reviewer',
'is:starred',
'is:watched',
+ 'is:wip',
'label:',
'message:',
'owner:',
@@ -83,6 +84,10 @@
'tr:',
];
+ // All of the ops, with corresponding negations.
+ const SEARCH_OPERATORS_WITH_NEGATIONS =
+ SEARCH_OPERATORS.concat(SEARCH_OPERATORS.map(op => `-${op}`));
+
const SELF_EXPRESSION = 'self';
const ME_EXPRESSION = 'me';
@@ -269,7 +274,7 @@
return this._fetchAccounts(predicate, expression);
default:
- return Promise.resolve(SEARCH_OPERATORS
+ return Promise.resolve(SEARCH_OPERATORS_WITH_NEGATIONS
.filter(operator => operator.includes(input)));
}
},
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
index 9ad0bf9..ca22942 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
@@ -69,7 +69,7 @@
.authorName,
.draftLabel,
.draftTooltip {
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
.draftLabel,
.draftTooltip {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
index 99a7054..f9ca92e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
@@ -45,7 +45,7 @@
}
.header {
border-bottom: 1px solid #ddd;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
.mainContainer {
padding: 1em 0;
@@ -69,7 +69,7 @@
justify-content: space-between;
}
.beta {
- font-weight: bold;
+ font-family: var(--font-family-bold);
color: #888;
}
</style>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
index b5da3a0..10d30e4 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -113,7 +113,7 @@
}
.dropdown-content a[selected] {
color: #000;
- font-weight: bold;
+ font-family: var(--font-family-bold);
pointer-events: none;
text-decoration: none;
}
@@ -194,7 +194,7 @@
.mobileNavLink {
color: #000;
font-size: 1.5em;
- font-weight: bold;
+ font-family: var(--font-family-bold);
text-decoration: none;
}
.mobileNavLink:not([href]) {
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 406a4a7..6f8a4a1 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -91,6 +91,7 @@
}
main {
flex: 1;
+ padding-bottom: 2em;
position: relative;
}
.errorView {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
index 34f0b16..1bd345e 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
@@ -36,7 +36,7 @@
}
header {
border-bottom: 1px solid #ddd;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
header,
main,
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
index bf5e4e0..51fa616 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
@@ -53,7 +53,7 @@
}
.action {
color: #a1c2fa;
- font-weight: bold;
+ font-family: var(--font-family-bold);
margin-left: 1em;
text-decoration: none;
}
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index c3031e2..6df6c98 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -135,7 +135,22 @@
type: Boolean,
value: false,
},
+
+ /** The DOM element of the selected suggestion. */
_selected: Object,
+
+ _textChangedSinceCommit: {
+ type: Boolean,
+ value: false,
+ },
+ },
+
+ observers: [
+ '_textChanged(text)',
+ ],
+
+ _textChanged() {
+ this._textChangedSinceCommit = true;
},
attached() {
@@ -275,6 +290,9 @@
* @param {boolean=} opt_tabComplete
*/
_handleInputCommit(opt_tabComplete) {
+ // Nothing to do if new input hasn't been entered.
+ if (!this._textChangedSinceCommit) { return; }
+
this._selected = this.$.suggestions.getCursorTarget();
this._commit(opt_tabComplete);
},
@@ -341,6 +359,8 @@
if (!opt_silent) {
this.fire('commit', {value});
}
+
+ this._textChangedSinceCommit = false;
},
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
index 75e221b..038962c 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -296,6 +296,32 @@
assert.isFalse(element._computeSuggestionsHidden(suggestions, true));
});
+ test('changing input sets _textChangedSinceCommit', () => {
+ element._textChangedSinceCommit = false;
+ element.text = 'foo bar baz';
+ assert.isTrue(element._textChangedSinceCommit);
+ });
+
+ test('_handleInputCommit with no change does nothing', () => {
+ const commitStub = sandbox.stub(element, '_commit');
+ element._textChangedSinceCommit = false;
+ element._handleInputCommit();
+ assert.isFalse(commitStub.called);
+ });
+
+ test('_textChangedSinceCommit reset after commit', () => {
+ element._textChangedSinceCommit = true;
+ element._commit();
+ assert.isFalse(element._textChangedSinceCommit);
+ });
+
+ test('_handleInputCommit with change to text', () => {
+ const commitStub = sandbox.stub(element, '_commit');
+ element._textChangedSinceCommit = true;
+ element._handleInputCommit();
+ assert.isTrue(commitStub.calledOnce);
+ });
+
suite('focus', () => {
let commitSpy;
let focusSpy;
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index 1fecbe7..d16e57f 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -30,9 +30,8 @@
color: #333;
cursor: pointer;
display: inline-block;
- font-family: var(--font-family);
+ font-family: var(--font-family-bold);
font-size: 12px;
- font-weight: bold;
outline-width: 0;
padding: .4em .85em;
position: relative;
@@ -65,7 +64,7 @@
border: none;
color: #00f;
font-size: inherit;
- font-weight: normal;
+ font-family: var(--font-family);
padding: 0;
text-decoration: underline;
}
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html
index d4a98e4..27c0355 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html
@@ -33,7 +33,7 @@
header {
border-bottom: 1px solid #ddd;
flex-shrink: 0;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
main {
display: flex;
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html
index 05075bd..68c3848 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html
@@ -40,11 +40,11 @@
display: block;
}
label {
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
li[selected] gr-button {
color: #000;
- font-weight: bold;
+ font-family: var(--font-family-bold);
text-decoration: none;
}
.schemes {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
index 8f2994d..f4813fa 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
@@ -61,7 +61,7 @@
list-style: none;
}
ul .accountName {
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
li .accountInfo,
li .itemAction {
@@ -92,7 +92,7 @@
padding: .85em 1em;
}
.bold-text {
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
:host:not([down-arrow]) .downArrow { display: none; }
:host([down-arrow]) .downArrow {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
index 21aa6cb..3570081 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
@@ -25,6 +25,14 @@
this._payloadCache = new Map();
}
+ /**
+ * Get or upgrade fetch options to include an ETag in a request.
+ * @param {string} url The URL being fetched.
+ * @param {!Object=} opt_options Optional options object in which to include
+ * the ETag request header. If omitted, the result will be a fresh option
+ * set.
+ * @return {!Object}
+ */
GrEtagDecorator.prototype.getOptions = function(url, opt_options) {
const etag = this._etags.get(url);
if (!etag) {
@@ -36,6 +44,16 @@
return options;
};
+ /**
+ * Handle a response to a request with ETag headers, potentially incorporating
+ * its result in the payload cache.
+ *
+ * @param {string} url The URL of the request.
+ * @param {!Response} response The response object.
+ * @param {string} payload The raw, unparsed JSON contained in the response
+ * body. Note: because response.text() cannot be read twice, this must be
+ * provided separately.
+ */
GrEtagDecorator.prototype.collect = function(url, response, payload) {
if (!response ||
!response.ok ||
@@ -54,19 +72,19 @@
}
};
+ /**
+ * Get the cached payload for a given URL.
+ * @param {string} url
+ * @return {string|undefined} Returns the unparsed JSON payload from the
+ * cache.
+ */
GrEtagDecorator.prototype.getCachedPayload = function(url) {
- let payload = this._payloadCache.get(url);
-
- if (typeof payload === 'object') {
- // Note: For the sake of cache transparency, deep clone the response
- // object so that cache hits are not equal object references. Some code
- // expects every network response to deserialize to a fresh object.
- payload = JSON.parse(JSON.stringify(payload));
- }
-
- return payload;
+ return this._payloadCache.get(url);
};
+ /**
+ * Limit the cache size to MAX_CACHE_SIZE.
+ */
GrEtagDecorator.prototype._truncateCache = function() {
for (const url of this._etags.keys()) {
if (this._etags.size <= MAX_CACHE_SIZE) {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
index 40e639e..77edae7 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
@@ -92,25 +92,5 @@
etag.collect('/foo', fakeRequest('bar', 200), 'new payload');
assert.strictEqual(etag.getCachedPayload('/foo'), 'new payload');
});
-
- test('getCachedPayload does not preserve object equality', () => {
- const payload = {foo: 'bar'};
- etag.collect('/foo', fakeRequest('bar'), payload);
- assert.deepEqual(etag.getCachedPayload('/foo'), payload);
- assert.notStrictEqual(etag.getCachedPayload('/foo'), payload);
- etag.collect('/foo', fakeRequest('bar', 304), {foo: 'baz'});
- assert.deepEqual(etag.getCachedPayload('/foo'), payload);
- assert.notStrictEqual(etag.getCachedPayload('/foo'), payload);
- etag.collect('/foo', fakeRequest('bar', 200), {foo: 'bar baz'});
- assert.deepEqual(etag.getCachedPayload('/foo'), {foo: 'bar baz'});
- assert.notStrictEqual(etag.getCachedPayload('/foo'), {foo: 'bar baz'});
- });
-
- test('getCachedPayload clones the response deeply', () => {
- const payload = {foo: {bar: 'baz'}};
- etag.collect('/foo', fakeRequest('bar'), payload);
- assert.deepEqual(etag.getCachedPayload('/foo'), payload);
- assert.notStrictEqual(etag.getCachedPayload('/foo').foo, payload.foo);
- });
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 684a967..210c42f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -186,17 +186,34 @@
* @return {?}
*/
getResponseObject(response) {
+ return this._readResponsePayload(response)
+ .then(payload => payload.parsed);
+ },
+
+ /**
+ * @param {!Object} response
+ * @return {!Object}
+ */
+ _readResponsePayload(response) {
return response.text().then(text => {
let result;
try {
- result = JSON.parse(text.substring(JSON_PREFIX.length));
+ result = this._parsePrefixedJSON(text);
} catch (_) {
result = null;
}
- return result;
+ return {parsed: result, raw: text};
});
},
+ /**
+ * @param {string} source
+ * @return {?}
+ */
+ _parsePrefixedJSON(source) {
+ return JSON.parse(source.substring(JSON_PREFIX.length));
+ },
+
getConfig() {
return this._fetchSharedCacheURL('/config/server/info');
},
@@ -820,8 +837,8 @@
this._etags.getOptions(urlWithParams))
.then(response => {
if (response && response.status === 304) {
- return Promise.resolve(
- this._etags.getCachedPayload(urlWithParams));
+ return Promise.resolve(this._parsePrefixedJSON(
+ this._etags.getCachedPayload(urlWithParams)));
}
if (response && !response.ok) {
@@ -834,13 +851,17 @@
}
const payloadPromise = response ?
- this.getResponseObject(response) :
- Promise.resolve();
- payloadPromise.then(payload => {
- this._etags.collect(urlWithParams, response, payload);
+ this._readResponsePayload(response) :
+ Promise.resolve(null);
+
+ return payloadPromise.then(payload => {
+ if (!payload) { return null; }
+
+ this._etags.collect(urlWithParams, response, payload.raw);
this._maybeInsertInLookup(payload);
+
+ return payload.parsed;
});
- return payloadPromise;
});
});
},
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index b63258f..2058f59 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -762,25 +762,90 @@
});
});
- test('_getChangeDetail passes params to ETags decorator', () => {
- const changeNum = 4321;
- element._projectLookup[changeNum] = 'test';
- const params = {foo: 'bar'};
- const expectedUrl = '/changes/test~4321/detail?foo=bar';
- sandbox.stub(element._etags, 'getOptions');
- sandbox.stub(element._etags, 'collect');
- return element._getChangeDetail(changeNum, params).then(() => {
- assert.isTrue(element._etags.getOptions.calledWithExactly(expectedUrl));
- assert.equal(element._etags.collect.lastCall.args[0], expectedUrl);
+ suite('_getChangeDetail', () => {
+ test('_getChangeDetail passes params to ETags decorator', () => {
+ const changeNum = 4321;
+ element._projectLookup[changeNum] = 'test';
+ const params = {foo: 'bar'};
+ const expectedUrl = '/changes/test~4321/detail?foo=bar';
+ sandbox.stub(element._etags, 'getOptions');
+ sandbox.stub(element._etags, 'collect');
+ return element._getChangeDetail(changeNum, params).then(() => {
+ assert.isTrue(element._etags.getOptions.calledWithExactly(
+ expectedUrl));
+ assert.equal(element._etags.collect.lastCall.args[0], expectedUrl);
+ });
});
- });
- test('_getChangeDetail calls errFn on 500', () => {
- const errFn = sinon.stub();
- sandbox.stub(element, '_fetchRawJSON')
- .returns(Promise.resolve({ok: false, status: 500}));
- return element._getChangeDetail(123, {}, errFn).then(() => {
- assert.isTrue(errFn.called);
+ test('_getChangeDetail calls errFn on 500', () => {
+ const errFn = sinon.stub();
+ sandbox.stub(element, '_fetchRawJSON')
+ .returns(Promise.resolve({ok: false, status: 500}));
+ return element._getChangeDetail(123, {}, errFn).then(() => {
+ assert.isTrue(errFn.called);
+ });
+ });
+
+ test('_getChangeDetail populates _projectLookup', () => {
+ sandbox.stub(element, '_fetchRawJSON')
+ .returns(Promise.resolve({ok: true}));
+
+ const mockResponse = {_number: 1, project: 'test'};
+ sandbox.stub(element, '_readResponsePayload').returns(Promise.resolve({
+ parsed: mockResponse,
+ raw: JSON.stringify(mockResponse),
+ }));
+ return element._getChangeDetail(1).then(() => {
+ assert.equal(Object.keys(element._projectLookup).length, 1);
+ assert.equal(element._projectLookup[1], 'test');
+ });
+ });
+
+ suite('_getChangeDetail ETag cache', () => {
+ let requestUrl;
+ let mockResponseSerial;
+ let collectSpy;
+ let getPayloadSpy;
+
+ setup(() => {
+ requestUrl = '/foo/bar';
+ const mockResponse = {foo: 'bar', baz: 42};
+ mockResponseSerial = element.JSON_PREFIX +
+ JSON.stringify(mockResponse);
+ sandbox.stub(element, '_urlWithParams').returns(requestUrl);
+ sandbox.stub(element, 'getChangeActionURL')
+ .returns(Promise.resolve(requestUrl));
+ collectSpy = sandbox.spy(element._etags, 'collect');
+ getPayloadSpy = sandbox.spy(element._etags, 'getCachedPayload');
+ });
+
+ test('contributes to cache', () => {
+ sandbox.stub(element, '_fetchRawJSON').returns(Promise.resolve({
+ text: () => Promise.resolve(mockResponseSerial),
+ status: 200,
+ ok: true,
+ }));
+
+ return element._getChangeDetail(123, {}).then(detail => {
+ assert.isFalse(getPayloadSpy.called);
+ assert.isTrue(collectSpy.calledOnce);
+ const cachedResponse = element._etags.getCachedPayload(requestUrl);
+ assert.equal(cachedResponse, mockResponseSerial);
+ });
+ });
+
+ test('uses cache on HTTP 304', () => {
+ sandbox.stub(element, '_fetchRawJSON').returns(Promise.resolve({
+ text: () => Promise.resolve(mockResponseSerial),
+ status: 304,
+ ok: true,
+ }));
+
+ return element._getChangeDetail(123, {}).then(detail => {
+ assert.isFalse(collectSpy.called);
+ assert.isTrue(getPayloadSpy.calledOnce);
+ });
+ });
});
});
@@ -858,17 +923,6 @@
});
});
- test('getChangeDetail populates _projectLookup', () => {
- sandbox.stub(element, '_fetchRawJSON')
- .returns(Promise.resolve({ok: true}));
- sandbox.stub(element, 'getResponseObject')
- .returns(Promise.resolve({_number: 1, project: 'test'}));
- return element._getChangeDetail(1).then(() => {
- assert.equal(Object.keys(element._projectLookup).length, 1);
- assert.equal(element._projectLookup[1], 'test');
- });
- });
-
test('_getChangeURLAndFetch', () => {
element._projectLookup = {1: 'test'};
const fetchStub = sandbox.stub(element, 'fetchJSON')
@@ -886,5 +940,24 @@
'/changes/test~1/revisions/1/test'));
});
});
+
+ suite('reading responses', () => {
+ test('_readResponsePayload', () => {
+ const mockObject = {foo: 'bar', baz: 'foo'};
+ const serial = element.JSON_PREFIX + JSON.stringify(mockObject);
+ const mockResponse = {text: () => Promise.resolve(serial)};
+ return element._readResponsePayload(mockResponse).then(payload => {
+ assert.deepEqual(payload.parsed, mockObject);
+ assert.equal(payload.raw, serial);
+ });
+ });
+
+ test('_parsePrefixedJSON', () => {
+ const obj = {x: 3, y: {z: 4}, w: 23};
+ const serial = element.JSON_PREFIX + JSON.stringify(obj);
+ const result = element._parsePrefixedJSON(serial);
+ assert.deepEqual(result, obj);
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/styles/app-theme.html b/polygerrit-ui/app/styles/app-theme.html
index db83b0c..4318757 100644
--- a/polygerrit-ui/app/styles/app-theme.html
+++ b/polygerrit-ui/app/styles/app-theme.html
@@ -30,6 +30,7 @@
--view-background-color: #fff;
--default-horizontal-margin: 1rem;
--font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
+ --font-family-bold: 'Roboto Medium', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
--monospace-font-family: 'Roboto Mono', Menlo, 'Lucida Console', Monaco, monospace;
--iron-overlay-backdrop: {
transition: none;
diff --git a/polygerrit-ui/app/styles/gr-change-list-styles.html b/polygerrit-ui/app/styles/gr-change-list-styles.html
index d283aac..00ea613 100644
--- a/polygerrit-ui/app/styles/gr-change-list-styles.html
+++ b/polygerrit-ui/app/styles/gr-change-list-styles.html
@@ -22,7 +22,7 @@
.topHeader,
.groupHeader {
border-bottom: 1px solid #eee;
- font-weight: bold;
+ font-family: var(--font-family-bold);
padding: .3em .5em;
}
.topHeader {
diff --git a/polygerrit-ui/app/styles/gr-form-styles.html b/polygerrit-ui/app/styles/gr-form-styles.html
index 7e0bf5a..603f610 100644
--- a/polygerrit-ui/app/styles/gr-form-styles.html
+++ b/polygerrit-ui/app/styles/gr-form-styles.html
@@ -39,7 +39,7 @@
}
.gr-form-styles .title {
color: #666;
- font-weight: bold;
+ font-family: var(--font-family-bold);
padding-right: .5em;
width: 15em;
}
diff --git a/polygerrit-ui/app/styles/gr-page-nav-styles.html b/polygerrit-ui/app/styles/gr-page-nav-styles.html
index e6e9c96..0c4d20f 100644
--- a/polygerrit-ui/app/styles/gr-page-nav-styles.html
+++ b/polygerrit-ui/app/styles/gr-page-nav-styles.html
@@ -44,14 +44,14 @@
margin-top: 1em;
}
.navStyles .title {
- font-weight: bold;
+ font-family: var(--font-family-bold);
margin: .4em 0;
}
.navStyles .selected {
background-color: #fff;
border-bottom: 1px dotted #808080;
border-top: 1px dotted #808080;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
.navStyles a {
color: black;
diff --git a/polygerrit-ui/app/styles/gr-table-styles.html b/polygerrit-ui/app/styles/gr-table-styles.html
index d296530..6b8c88d0 100644
--- a/polygerrit-ui/app/styles/gr-table-styles.html
+++ b/polygerrit-ui/app/styles/gr-table-styles.html
@@ -34,7 +34,7 @@
.genericList th {
background-color: #ddd;
border-bottom: 1px solid #eee;
- font-weight: bold;
+ font-family: var(--font-family-bold);
padding: .3em .5em;
text-align: left;
}
diff --git a/polygerrit-ui/app/styles/shared-styles.html b/polygerrit-ui/app/styles/shared-styles.html
index 9725251..5c11d60 100644
--- a/polygerrit-ui/app/styles/shared-styles.html
+++ b/polygerrit-ui/app/styles/shared-styles.html
@@ -65,15 +65,15 @@
/* Other Shared Styles*/
h1 {
font-size: 2em;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
h2 {
font-size: 1.5em;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
h3 {
font-size: 1.17em;
- font-weight: bold;
+ font-family: var(--font-family-bold);
}
/* Stopgap solution until we remove hidden$ attributes. */
[hidden] {