Merge "Allow admins to index a change even if the branch is not Readable for them" into stable-2.16
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 3ec989e..7be8c32 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1376,6 +1376,28 @@
}
----
+[[index.changes]]
+=== Index a set of changes
+
+This endpoint allows Gerrit admins to index a set of changes with one request
+by providing a link:#index-changes-input[IndexChangesInput] entity.
+
+Using this endpoint Gerrit admins can also index change(s) which are not visible to them.
+
+.Request
+----
+ POST /config/server/index.changes HTTP/1.0
+ Content-Type: application/json; charset=UTF-8
+
+ {changes: ["foo~101", "bar~202"]}
+----
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+----
+
[[ids]]
== IDs
@@ -1825,6 +1847,17 @@
Only set for disk caches.
|==================================
+[[index-changes-input]]
+=== IndexChangesInput
+The `IndexChangesInput` contains a list of numerical changes IDs to index.
+
+[options="header",cols="1,^2,4"]
+|================================
+|Field Name ||Description
+|`changes` ||
+List of link:rest-api-changes.html#change-id[change-ids]
+|================================
+
[[jvm-summary-info]]
=== JvmSummaryInfo
The `JvmSummaryInfo` entity contains information about the JVM.
diff --git a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
index 286b045..27ed603 100644
--- a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
+++ b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
@@ -37,7 +37,7 @@
countsByChange.clear();
}
- long getCount(ChangeInfo info) {
+ public long getCount(ChangeInfo info) {
return countsByChange.get(info._number);
}
diff --git a/java/com/google/gerrit/server/restapi/config/IndexChanges.java b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
new file mode 100644
index 0000000..b178118
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
@@ -0,0 +1,83 @@
+// Copyright (C) 2019 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.restapi.config;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.ChangeFinder;
+import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.config.IndexChanges.Input;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.util.Set;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+@Singleton
+public class IndexChanges implements RestModifyView<ConfigResource, Input> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ public static class Input {
+ public Set<String> changes;
+ }
+
+ private final ChangeFinder changeFinder;
+ private final SchemaFactory<ReviewDb> schemaFactory;
+ private final ChangeData.Factory changeDataFactory;
+ private final ChangeIndexer indexer;
+
+ @Inject
+ IndexChanges(
+ ChangeFinder changeFinder,
+ SchemaFactory<ReviewDb> schemaFactory,
+ ChangeData.Factory changeDataFactory,
+ ChangeIndexer indexer) {
+ this.changeFinder = changeFinder;
+ this.schemaFactory = schemaFactory;
+ this.changeDataFactory = changeDataFactory;
+ this.indexer = indexer;
+ }
+
+ @Override
+ public Object apply(ConfigResource resource, Input input) throws OrmException {
+ if (input == null || input.changes == null) {
+ return Response.ok("Nothing to index");
+ }
+
+ try (ReviewDb db = schemaFactory.open()) {
+ for (String id : input.changes) {
+ for (ChangeNotes n : changeFinder.find(id)) {
+ try {
+ indexer.index(changeDataFactory.create(db, n));
+ logger.atFine().log("Indexed change %s", id);
+ } catch (IOException e) {
+ logger.atSevere().withCause(e).log("Failed to index change %s", id);
+ }
+ }
+ }
+ }
+
+ return Response.ok("Indexed changes " + input.changes);
+ }
+}
diff --git a/java/com/google/gerrit/server/restapi/config/Module.java b/java/com/google/gerrit/server/restapi/config/Module.java
index c4a6f56..78a7f48 100644
--- a/java/com/google/gerrit/server/restapi/config/Module.java
+++ b/java/com/google/gerrit/server/restapi/config/Module.java
@@ -37,6 +37,7 @@
get(CONFIG_KIND, "version").to(GetVersion.class);
get(CONFIG_KIND, "info").to(GetServerInfo.class);
post(CONFIG_KIND, "check.consistency").to(CheckConsistency.class);
+ post(CONFIG_KIND, "index.changes").to(IndexChanges.class);
post(CONFIG_KIND, "reload").to(ReloadConfig.class);
get(CONFIG_KIND, "preferences").to(GetPreferences.class);
put(CONFIG_KIND, "preferences").to(SetPreferences.class);
diff --git a/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
index 4a2c81b..2e5ea00 100644
--- a/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
@@ -57,7 +57,8 @@
RestCall.get("/config/server/capabilities"),
RestCall.get("/config/server/caches"),
RestCall.post("/config/server/caches"),
- RestCall.get("/config/server/tasks"));
+ RestCall.get("/config/server/tasks"),
+ RestCall.post("/config/server/index.changes"));
/**
* Cache REST endpoints to be tested, the URLs contain a placeholder for the cache identifier.
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
new file mode 100644
index 0000000..fa35d19
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
@@ -0,0 +1,98 @@
+// Copyright (C) 2019 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.rest.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ChangeIndexedCounter;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.events.ChangeIndexedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.restapi.config.IndexChanges;
+import com.google.inject.Inject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class IndexChangesIT extends AbstractDaemonTest {
+
+ @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
+
+ private ChangeIndexedCounter changeIndexedCounter;
+ private RegistrationHandle changeIndexedCounterHandle;
+
+ @Before
+ public void addChangeIndexedCounter() {
+ changeIndexedCounter = new ChangeIndexedCounter();
+ changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter);
+ }
+
+ @After
+ public void removeChangeIndexedCounter() {
+ if (changeIndexedCounterHandle != null) {
+ changeIndexedCounterHandle.remove();
+ }
+ }
+
+ @Test
+ public void indexRequestFromNonAdminRejected() throws Exception {
+ String changeId = createChange().getChangeId();
+ IndexChanges.Input in = new IndexChanges.Input();
+ in.changes = ImmutableSet.of(changeId);
+ changeIndexedCounter.clear();
+ userRestSession.post("/config/server/index.changes", in).assertForbidden();
+ assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(0);
+ }
+
+ @Test
+ public void indexVisibleChange() throws Exception {
+ String changeId = createChange().getChangeId();
+ IndexChanges.Input in = new IndexChanges.Input();
+ in.changes = ImmutableSet.of(changeId);
+ changeIndexedCounter.clear();
+ adminRestSession.post("/config/server/index.changes", in).assertOK();
+ assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+ }
+
+ @Test
+ public void indexNonVisibleChange() throws Exception {
+ String changeId = createChange().getChangeId();
+ ChangeInfo changeInfo = info(changeId);
+ blockRead("refs/heads/master");
+ IndexChanges.Input in = new IndexChanges.Input();
+ changeIndexedCounter.clear();
+ in.changes = ImmutableSet.of(changeId);
+ adminRestSession.post("/config/server/index.changes", in).assertOK();
+ assertThat(changeIndexedCounter.getCount(changeInfo)).isEqualTo(1);
+ }
+
+ @Test
+ public void indexMultipleChanges() throws Exception {
+ ImmutableSet.Builder<String> changeIds = ImmutableSet.builder();
+ for (int i = 0; i < 10; i++) {
+ changeIds.add(createChange().getChangeId());
+ }
+ IndexChanges.Input in = new IndexChanges.Input();
+ in.changes = changeIds.build();
+ changeIndexedCounter.clear();
+ adminRestSession.post("/config/server/index.changes", in).assertOK();
+ for (String changeId : in.changes) {
+ assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+ }
+ }
+}