Merge "Upgrade protobuf-java to 3.4.0"
diff --git a/Documentation/config-accounts.txt b/Documentation/config-accounts.txt
index 9743283..150bf88 100644
--- a/Documentation/config-accounts.txt
+++ b/Documentation/config-accounts.txt
@@ -403,6 +403,18 @@
notedb.accounts.sequenceBatchSize] parameter in the `gerrit.config`
file.
+[[replication]]
+== Replication
+
+To replicate account data the following branches from the `All-Users`
+repository must be replicated:
+
+* `refs/users/*` (user branches)
+* `refs/meta/external-ids` (external IDs)
+* `refs/starred-changes/*` (star labels)
+* `refs/sequences/accounts` (account sequence numbers, not needed for Gerrit
+ slaves)
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
index 0788cb8..fc43b97 100644
--- a/Documentation/database-setup.txt
+++ b/Documentation/database-setup.txt
@@ -68,7 +68,7 @@
mysql
CREATE USER 'gerrit'@'localhost' IDENTIFIED BY 'secret';
- CREATE DATABASE reviewdb;
+ CREATE DATABASE reviewdb DEFAULT CHARACTER SET 'utf8';
GRANT ALL ON reviewdb.* TO 'gerrit'@'localhost';
FLUSH PRIVILEGES;
----
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 1a026d1..983e5e2 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -407,6 +407,10 @@
+
Update of the group secondary index
+* `com.google.gerrit.server.extensions.events.ProjectIndexedListener`:
++
+Update of the project secondary index
+
* `com.google.gerrit.httpd.WebLoginListener`:
+
User login or logout interactively on the Web user interface.
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 9d76d34..6e256f1 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -324,6 +324,65 @@
}
----
+[[query-projects]]
+=== Query Projects
+--
+'GET /projects/?query=<query>'
+--
+
+Queries projects visible to the caller. The
+link:user-search-projects.html#_search_operators[query string] must be
+provided by the `query` parameter. The `start` and `limit` parameters
+can be used to skip/limit results.
+
+As result a list of link:#project-info[ProjectInfo] entities is returned.
+
+.Request
+----
+ GET /projects/?query=name:test HTTP/1.0
+----
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "test": {
+ "id": "test",
+ "description": "\u003chtml\u003e is escaped"
+ }
+ }
+----
+
+[[project-query-limit]]
+==== Project Limit
+The `/projects/?query=<query>` URL also accepts a limit integer in the
+`limit` parameter. This limits the results to `limit` projects.
+
+Query the first 25 projects in project list.
+----
+ GET /projects/?query=<query>&limit=25 HTTP/1.0
+----
+
+The `/projects/` URL also accepts a start integer in the `start`
+parameter. The results will skip `start` groups from project list.
+
+Query 25 projects starting from index 50.
+----
+ GET /groups/?query=<query>&limit=25&start=50 HTTP/1.0
+----
+
+[[project-query-options]]
+==== Project Options
+Additional fields can be obtained by adding `o` parameters. Each option
+requires more lookups and slows down the query response time to the
+client so they are generally disabled by default. The supported fields
+are described in the context of the link:#project-options[List Projects]
+REST endpoint.
+
[[get-project]]
=== Get Project
--
diff --git a/Documentation/user-search-projects.txt b/Documentation/user-search-projects.txt
new file mode 100644
index 0000000..eff64fa
--- /dev/null
+++ b/Documentation/user-search-projects.txt
@@ -0,0 +1,36 @@
+= Gerrit Code Review - Searching Projects
+
+[[search-operators]]
+== Search Operators
+
+Operators act as restrictions on the search. As more operators
+are added to the same query string, they further restrict the
+returned results.
+
+[[name]]
+name:'NAME'::
++
+Matches projects that have the NAME 'NAME'.
+
+== Magical Operators
+
+[[is-visible]]
+is:visible::
++
+Magical internal flag to prove the current user has access to read
+the projects and all the refs. This flag is always added to any query.
+
+[[limit]]
+limit:'CNT'::
++
+Limit the returned results to no more than 'CNT' records. This is
+automatically set to the page size configured in the current user's
+preferences. Including it in a web query may lead to unpredictable
+results with regards to pagination.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/WORKSPACE b/WORKSPACE
index 58ee261..d0d797f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,5 +1,9 @@
workspace(name = "gerrit")
+load("//:version.bzl", "check_version")
+
+check_version("0.5.3")
+
load("//tools/bzl:maven_jar.bzl", "maven_jar", "GERRIT", "MAVEN_LOCAL")
load("//lib/codemirror:cm.bzl", "CM_VERSION", "DIFF_MATCH_PATCH_VERSION")
load("//plugins:external_plugin_deps.bzl", "external_plugin_deps")
@@ -175,8 +179,8 @@
maven_jar(
name = "gson",
- artifact = "com.google.code.gson:gson:2.8.0",
- sha1 = "c4ba5371a29ac9b2ad6129b1d39ea38750043eff",
+ artifact = "com.google.code.gson:gson:2.8.2",
+ sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf",
)
maven_jar(
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 84f3533..22d27ac 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -38,6 +38,7 @@
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
@@ -67,6 +68,7 @@
import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.common.SshKeyInfo;
import com.google.gerrit.extensions.events.AccountIndexedListener;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -79,6 +81,8 @@
import com.google.gerrit.gpg.testutil.TestKey;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.account.AccountConfig;
@@ -106,6 +110,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -155,6 +160,8 @@
@Inject private DynamicSet<AccountIndexedListener> accountIndexedListeners;
+ @Inject private DynamicSet<GitReferenceUpdatedListener> refUpdateListeners;
+
@Inject private Sequences seq;
@Inject private Provider<InternalAccountQuery> accountQueryProvider;
@@ -163,6 +170,8 @@
private AccountIndexedCounter accountIndexedCounter;
private RegistrationHandle accountIndexEventCounterHandle;
+ private RefUpdateCounter refUpdateCounter;
+ private RegistrationHandle refUpdateCounterHandle;
private ExternalIdsUpdate externalIdsUpdate;
private List<ExternalId> savedExternalIds;
@@ -180,6 +189,19 @@
}
@Before
+ public void addRefUpdateCounter() {
+ refUpdateCounter = new RefUpdateCounter();
+ refUpdateCounterHandle = refUpdateListeners.add(refUpdateCounter);
+ }
+
+ @After
+ public void removeRefUpdateCounter() {
+ if (refUpdateCounterHandle != null) {
+ refUpdateCounterHandle.remove();
+ }
+ }
+
+ @Before
public void saveExternalIds() throws Exception {
externalIdsUpdate = externalIdsUpdateFactory.create();
@@ -228,16 +250,29 @@
@Test
public void create() throws Exception {
- create(2); // account creation + external ID creation
+ Account.Id accountId = create(2); // account creation + external ID creation
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)),
+ RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS),
+ RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS));
}
@Test
@UseSsh
public void createWithSshKeys() throws Exception {
- create(3); // account creation + external ID creation + adding SSH keys
+ Account.Id accountId = create(3); // account creation + external ID creation + adding SSH keys
+ refUpdateCounter.assertRefUpdateFor(
+ ImmutableMap.of(
+ RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)),
+ 2,
+ RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS),
+ 1,
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS),
+ 1));
}
- private void create(int expectedAccountReindexCalls) throws Exception {
+ private Account.Id create(int expectedAccountReindexCalls) throws Exception {
String name = "foo";
TestAccount foo = accountCreator.create(name);
AccountInfo info = gApi.accounts().id(foo.id.get()).get();
@@ -245,6 +280,7 @@
assertThat(info.name).isEqualTo(name);
accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
assertUserBranch(foo.getId(), name, null);
+ return foo.getId();
}
@Test
@@ -378,15 +414,24 @@
public void starUnstarChange() throws Exception {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
+ refUpdateCounter.clear();
+
gApi.accounts().self().starChange(triplet);
ChangeInfo change = info(triplet);
assertThat(change.starred).isTrue();
assertThat(change.stars).contains(DEFAULT_LABEL);
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
gApi.accounts().self().unstarChange(triplet);
change = info(triplet);
assertThat(change.starred).isNull();
assertThat(change.stars).isNull();
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+
accountIndexedCounter.assertNoReindex();
}
@@ -394,6 +439,8 @@
public void starUnstarChangeWithLabels() throws Exception {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
+ refUpdateCounter.clear();
+
assertThat(gApi.accounts().self().getStars(triplet)).isEmpty();
assertThat(gApi.accounts().self().getStarredChanges()).isEmpty();
@@ -412,6 +459,9 @@
assertThat(starredChange._number).isEqualTo(r.getChange().getId().get());
assertThat(starredChange.starred).isTrue();
assertThat(starredChange.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder();
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
gApi.accounts()
.self()
@@ -428,6 +478,10 @@
assertThat(starredChange._number).isEqualTo(r.getChange().getId().get());
assertThat(starredChange.starred).isNull();
assertThat(starredChange.stars).containsExactly("red", "yellow").inOrder();
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+
accountIndexedCounter.assertNoReindex();
setApiUser(user);
@@ -1899,4 +1953,45 @@
assertThat(countsByAccount).isEmpty();
}
}
+
+ private static class RefUpdateCounter implements GitReferenceUpdatedListener {
+ private final AtomicLongMap<String> countsByProjectRefs = AtomicLongMap.create();
+
+ static String projectRef(Project.NameKey project, String ref) {
+ return projectRef(project.get(), ref);
+ }
+
+ static String projectRef(String project, String ref) {
+ return project + ":" + ref;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(Event event) {
+ countsByProjectRefs.incrementAndGet(projectRef(event.getProjectName(), event.getRefName()));
+ }
+
+ void clear() {
+ countsByProjectRefs.clear();
+ }
+
+ long getCount(String projectRef) {
+ return countsByProjectRefs.get(projectRef);
+ }
+
+ void assertRefUpdateFor(String... projectRefs) {
+ Map<String, Integer> expectedRefUpdateCounts = new HashMap<>();
+ for (String projectRef : projectRefs) {
+ expectedRefUpdateCounts.put(projectRef, 1);
+ }
+ assertRefUpdateFor(expectedRefUpdateCounts);
+ }
+
+ void assertRefUpdateFor(Map<String, Integer> expectedProjectRefUpdateCounts) {
+ for (Map.Entry<String, Integer> e : expectedProjectRefUpdateCounts.entrySet()) {
+ assertThat(getCount(e.getKey())).isEqualTo(e.getValue());
+ }
+ assertThat(countsByProjectRefs).hasSize(expectedProjectRefUpdateCounts.size());
+ clear();
+ }
+ }
}
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 c2d3184..baa0a68 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
@@ -41,6 +41,7 @@
import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
+import static com.google.gerrit.server.StarredChangesUtil.DEFAULT_LABEL;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
@@ -57,6 +58,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.AtomicLongMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.GerritConfig;
@@ -105,6 +107,7 @@
import com.google.gerrit.extensions.common.PureRevertInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.common.TrackingIdInfo;
+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.extensions.restapi.AuthException;
@@ -170,6 +173,11 @@
@Inject private DynamicSet<ChangeMessageModifier> changeMessageModifiers;
+ @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
+
+ private ChangeIndexedCounter changeIndexedCounter;
+ private RegistrationHandle changeIndexedCounterHandle;
+
@Before
public void setTimeForTesting() {
systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
@@ -181,6 +189,19 @@
System.setProperty("user.timezone", systemTimeZone);
}
+ @Before
+ public void addChangeIndexedCounter() {
+ changeIndexedCounter = new ChangeIndexedCounter();
+ changeIndexedCounterHandle = changeIndexedListeners.add(changeIndexedCounter);
+ }
+
+ @After
+ public void removeChangeIndexedCounter() {
+ if (changeIndexedCounterHandle != null) {
+ changeIndexedCounterHandle.remove();
+ }
+ }
+
@Test
public void reflog() throws Exception {
// Tests are using DfsRepository which does not implement getReflogReader,
@@ -3299,6 +3320,25 @@
}
@Test
+ public void starUnstar() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String triplet = project.get() + "~master~" + r.getChangeId();
+ changeIndexedCounter.clear();
+
+ gApi.accounts().self().starChange(triplet);
+ ChangeInfo change = info(triplet);
+ assertThat(change.starred).isTrue();
+ assertThat(change.stars).contains(DEFAULT_LABEL);
+ changeIndexedCounter.assertReindexOf(change);
+
+ gApi.accounts().self().unstarChange(triplet);
+ change = info(triplet);
+ assertThat(change.starred).isNull();
+ assertThat(change.stars).isNull();
+ changeIndexedCounter.assertReindexOf(change);
+ }
+
+ @Test
public void ignore() throws Exception {
TestAccount user2 = accountCreator.user2();
@@ -3480,4 +3520,36 @@
exception.expectMessage("invalid labels: " + invalidLabel);
gApi.accounts().self().setStars(changeId, new StarsInput(ImmutableSet.of(invalidLabel)));
}
+
+ private static class ChangeIndexedCounter implements ChangeIndexedListener {
+ private final AtomicLongMap<Integer> countsByChange = AtomicLongMap.create();
+
+ @Override
+ public void onChangeIndexed(int id) {
+ countsByChange.incrementAndGet(id);
+ }
+
+ @Override
+ public void onChangeDeleted(int id) {
+ countsByChange.incrementAndGet(id);
+ }
+
+ void clear() {
+ countsByChange.clear();
+ }
+
+ long getCount(ChangeInfo info) {
+ return countsByChange.get(info._number);
+ }
+
+ void assertReindexOf(ChangeInfo info) {
+ assertReindexOf(info, 1);
+ }
+
+ void assertReindexOf(ChangeInfo info, int expectedCount) {
+ assertThat(getCount(info)).isEqualTo(expectedCount);
+ assertThat(countsByChange).hasSize(1);
+ clear();
+ }
+ }
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
index 0fa09af..9b12069f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
@@ -28,6 +28,7 @@
import com.google.gerrit.extensions.api.plugins.Plugins.ListRequest;
import com.google.gerrit.extensions.common.InstallPluginInput;
import com.google.gerrit.extensions.common.PluginInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.RawInput;
@@ -45,7 +46,7 @@
private static final RawInput HTML_PLUGIN_CONTENT =
RawInputUtil.create(HTML_PLUGIN.getBytes(UTF_8));
- private static final List<String> PLUGINS =
+ private static final ImmutableList<String> PLUGINS =
ImmutableList.of(
"plugin-a.js", "plugin-b.html", "plugin-c.js", "plugin-d.html", "plugin_e.js");
@@ -107,12 +108,21 @@
api = gApi.plugins().name("plugin-a");
assertThat(api.get().disabled).isNull();
assertPlugins(list().get(), PLUGINS);
+
+ // Non-admin cannot disable
+ setApiUser(user);
+ try {
+ gApi.plugins().name("plugin-a").disable();
+ fail("Expected AuthException");
+ } catch (AuthException expected) {
+ // Expected
+ }
}
@Test
public void installNotAllowed() throws Exception {
exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("remote installation is disabled");
+ exception.expectMessage("remote plugin administration is disabled");
gApi.plugins().install("test.js", new InstallPluginInput());
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java
index 7d5072a..6f4495e 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java
@@ -18,11 +18,13 @@
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static java.util.stream.Collectors.toList;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.DashboardInfo;
+import com.google.gerrit.extensions.api.projects.DashboardSectionInfo;
import com.google.gerrit.extensions.api.projects.ProjectApi;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -57,14 +59,24 @@
@Test
public void getDashboard() throws Exception {
- DashboardInfo info = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test");
+ DashboardInfo info = createTestDashboard();
DashboardInfo result = project().dashboard(info.id).get();
assertDashboardInfo(result, info);
}
@Test
+ public void getDashboardWithNoDescription() throws Exception {
+ DashboardInfo info = newDashboardInfo(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test");
+ info.description = null;
+ DashboardInfo created = createDashboard(info);
+ assertThat(created.description).isNull();
+ DashboardInfo result = project().dashboard(created.id).get();
+ assertThat(result.description).isNull();
+ }
+
+ @Test
public void getDashboardNonDefault() throws Exception {
- DashboardInfo info = createDashboard("my", "test");
+ DashboardInfo info = createTestDashboard("my", "test");
DashboardInfo result = project().dashboard(info.id).get();
assertDashboardInfo(result, info);
}
@@ -72,15 +84,15 @@
@Test
public void listDashboards() throws Exception {
assertThat(dashboards()).isEmpty();
- DashboardInfo info1 = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test1");
- DashboardInfo info2 = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test2");
+ DashboardInfo info1 = createTestDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test1");
+ DashboardInfo info2 = createTestDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test2");
assertThat(dashboards().stream().map(d -> d.id).collect(toList()))
.containsExactly(info1.id, info2.id);
}
@Test
public void setDefaultDashboard() throws Exception {
- DashboardInfo info = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test");
+ DashboardInfo info = createTestDashboard();
assertThat(info.isDefault).isNull();
project().dashboard(info.id).setDefault();
assertThat(project().dashboard(info.id).get().isDefault).isTrue();
@@ -89,7 +101,7 @@
@Test
public void setDefaultDashboardByProject() throws Exception {
- DashboardInfo info = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test");
+ DashboardInfo info = createTestDashboard();
assertThat(info.isDefault).isNull();
project().defaultDashboard(info.id);
assertThat(project().dashboard(info.id).get().isDefault).isTrue();
@@ -104,8 +116,8 @@
@Test
public void replaceDefaultDashboard() throws Exception {
- DashboardInfo d1 = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test1");
- DashboardInfo d2 = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test2");
+ DashboardInfo d1 = createTestDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test1");
+ DashboardInfo d2 = createTestDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test2");
assertThat(d1.isDefault).isNull();
assertThat(d2.isDefault).isNull();
project().dashboard(d1.id).setDefault();
@@ -120,7 +132,7 @@
@Test
public void cannotGetDashboardWithInheritedForNonDefault() throws Exception {
- DashboardInfo info = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test");
+ DashboardInfo info = createTestDashboard();
exception.expect(BadRequestException.class);
exception.expectMessage("inherited flag can only be used with default");
project().dashboard(info.id).get(true);
@@ -132,6 +144,14 @@
assertThat(actual.ref).isEqualTo(expected.ref);
assertThat(actual.project).isEqualTo(project.get());
assertThat(actual.definingProject).isEqualTo(project.get());
+ assertThat(actual.description).isEqualTo(expected.description);
+ assertThat(actual.title).isEqualTo(expected.title);
+ assertThat(actual.foreach).isEqualTo(expected.foreach);
+ if (expected.sections == null) {
+ assertThat(actual.sections).isNull();
+ } else {
+ assertThat(actual.sections.size()).isEqualTo(expected.sections.size());
+ }
}
private List<DashboardInfo> dashboards() throws Exception {
@@ -142,8 +162,27 @@
return gApi.projects().name(project.get());
}
- private DashboardInfo createDashboard(String ref, String path) throws Exception {
+ private DashboardInfo newDashboardInfo(String ref, String path) {
DashboardInfo info = DashboardsCollection.newDashboardInfo(ref, path);
+ info.title = "Reviewer";
+ info.description = "Own review requests";
+ info.foreach = "owner:self";
+ DashboardSectionInfo section = new DashboardSectionInfo();
+ section.name = "Open";
+ section.query = "is:open";
+ info.sections = ImmutableList.of(section);
+ return info;
+ }
+
+ private DashboardInfo createTestDashboard() throws Exception {
+ return createTestDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test");
+ }
+
+ private DashboardInfo createTestDashboard(String ref, String path) throws Exception {
+ return createDashboard(newDashboardInfo(ref, path));
+ }
+
+ private DashboardInfo createDashboard(DashboardInfo info) throws Exception {
String canonicalRef = DashboardsCollection.normalizeDashboardRef(info.ref);
try {
project().branch(canonicalRef).create(new BranchInput());
@@ -156,14 +195,23 @@
try (Repository r = repoManager.openRepository(project)) {
TestRepository<Repository>.CommitBuilder cb =
new TestRepository<>(r).branch(canonicalRef).commit();
- String content =
- "[dashboard]\n"
- + "Title = Reviewer\n"
- + "Description = Own review requests\n"
- + "foreach = owner:self\n"
- + "[section \"Open\"]\n"
- + "query = is:open";
- cb.add(info.path, content);
+ StringBuilder content = new StringBuilder("[dashboard]\n");
+ if (info.title != null) {
+ content.append("title = ").append(info.title).append("\n");
+ }
+ if (info.description != null) {
+ content.append("description = ").append(info.description).append("\n");
+ }
+ if (info.foreach != null) {
+ content.append("foreach = ").append(info.foreach).append("\n");
+ }
+ if (info.sections != null) {
+ for (DashboardSectionInfo section : info.sections) {
+ content.append("[section \"").append(section.name).append("\"]\n");
+ content.append("query = ").append(section.query).append("\n");
+ }
+ }
+ cb.add(info.path, content.toString());
RevCommit c = cb.create();
project().commit(c.name());
}
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 95e4e16..928cd7e 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
@@ -1852,6 +1852,16 @@
.isEqualTo(Iterables.getLast(commits).name());
}
+ @Test
+ public void pushToPublishMagicBranchIsAllowed() throws Exception {
+ // Push to "refs/publish/*" will be a synonym of "refs/for/*".
+ createChange("refs/publish/master");
+ PushOneCommit.Result result = pushTo("refs/publish/master");
+ result.assertOkStatus();
+ assertThat(result.getMessage())
+ .endsWith("Pushing to refs/publish/* is deprecated, use refs/for/* instead.\n");
+ }
+
private DraftInput newDraft(String path, int line, String message) {
DraftInput d = new DraftInput();
d.path = path;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index 2fe9dcd..4c4bc94 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -55,6 +55,7 @@
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate.RefsMetaExternalIdsUpdate;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.LockFailureException;
import com.google.gson.reflect.TypeToken;
import com.google.gwtorm.server.OrmDuplicateKeyException;
@@ -579,7 +580,17 @@
noteMap.set(noteId, dataBlob);
ExternalIdsUpdate.commit(
- repo, rw, ins, rev, noteMap, "Add external ID", admin.getIdent(), admin.getIdent());
+ allUsers,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ "Add external ID",
+ admin.getIdent(),
+ admin.getIdent(),
+ null,
+ GitReferenceUpdated.DISABLED);
return noteId.getName();
}
}
@@ -600,7 +611,17 @@
noteMap.set(noteId, dataBlob);
ExternalIdsUpdate.commit(
- repo, rw, ins, rev, noteMap, "Add external ID", admin.getIdent(), admin.getIdent());
+ allUsers,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ "Add external ID",
+ admin.getIdent(),
+ admin.getIdent(),
+ null,
+ GitReferenceUpdated.DISABLED);
return noteId.getName();
}
}
@@ -617,7 +638,17 @@
noteMap.set(noteId, dataBlob);
ExternalIdsUpdate.commit(
- repo, rw, ins, rev, noteMap, "Add external ID", admin.getIdent(), admin.getIdent());
+ allUsers,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ "Add external ID",
+ admin.getIdent(),
+ admin.getIdent(),
+ null,
+ GitReferenceUpdated.DISABLED);
return noteId.getName();
}
}
@@ -634,7 +665,17 @@
noteMap.set(noteId, dataBlob);
ExternalIdsUpdate.commit(
- repo, rw, ins, rev, noteMap, "Add external ID", admin.getIdent(), admin.getIdent());
+ allUsers,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ "Add external ID",
+ admin.getIdent(),
+ admin.getIdent(),
+ null,
+ GitReferenceUpdated.DISABLED);
return noteId.getName();
}
}
@@ -690,6 +731,8 @@
new DisabledExternalIdCache(),
serverIdent.get(),
serverIdent.get(),
+ null,
+ GitReferenceUpdated.DISABLED,
() -> {
if (!doneBgUpdate.getAndSet(true)) {
try {
@@ -726,6 +769,8 @@
new DisabledExternalIdCache(),
serverIdent.get(),
serverIdent.get(),
+ null,
+ GitReferenceUpdated.DISABLED,
() -> {
try {
extIdsUpdate
@@ -824,7 +869,17 @@
NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev);
ExternalIdsUpdate.insert(rw, ins, noteMap, extId);
ExternalIdsUpdate.commit(
- repo, rw, ins, rev, noteMap, "insert new ID", serverIdent.get(), serverIdent.get());
+ allUsers,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ "insert new ID",
+ serverIdent.get(),
+ serverIdent.get(),
+ null,
+ GitReferenceUpdated.DISABLED);
}
}
@@ -839,6 +894,7 @@
}
ExternalIdsUpdate.commit(
+ allUsers,
testRepo.getRepository(),
testRepo.getRevWalk(),
ins,
@@ -846,7 +902,9 @@
noteMap,
"Add external ID",
admin.getIdent(),
- admin.getIdent());
+ admin.getIdent(),
+ null,
+ GitReferenceUpdated.DISABLED);
}
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
index 7de9d70..c02b60f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
@@ -18,13 +18,13 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
-import com.google.gerrit.server.account.PutUsername;
+import com.google.gerrit.extensions.common.UsernameInput;
import org.junit.Test;
public class PutUsernameIT extends AbstractDaemonTest {
@Test
public void set() throws Exception {
- PutUsername.Input in = new PutUsername.Input();
+ UsernameInput in = new UsernameInput();
in.username = "myUsername";
RestResponse r =
adminRestSession.put("/accounts/" + accountCreator.create().id.get() + "/username", in);
@@ -34,7 +34,7 @@
@Test
public void setExisting_Conflict() throws Exception {
- PutUsername.Input in = new PutUsername.Input();
+ UsernameInput in = new UsernameInput();
in.username = admin.username;
adminRestSession
.put("/accounts/" + accountCreator.create().id.get() + "/username", in)
@@ -43,7 +43,7 @@
@Test
public void setNew_MethodNotAllowed() throws Exception {
- PutUsername.Input in = new PutUsername.Input();
+ UsernameInput in = new UsernameInput();
in.username = "newUsername";
adminRestSession.put("/accounts/" + admin.username + "/username", in).assertMethodNotAllowed();
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
index 0324ffa..e60abc6 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
@@ -43,6 +43,7 @@
import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
import com.google.gerrit.extensions.client.Side;
import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Account;
@@ -326,7 +327,7 @@
public void restApiNotFoundWhenNoteDbDisabled() throws Exception {
PushOneCommit.Result r = createChange();
exception.expect(ResourceNotFoundException.class);
- rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Rebuild.Input());
+ rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Input());
}
@Test
@@ -336,7 +337,7 @@
setNotesMigration(true, false);
checker.assertNoChangeRef(project, id);
- rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Rebuild.Input());
+ rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Input());
checker.checkChanges(id);
}
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java
index 7868443..2d04e11 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.group.GroupIndex;
+import com.google.gerrit.server.index.project.ProjectIndex;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -75,6 +76,10 @@
new FactoryModuleBuilder()
.implement(GroupIndex.class, ElasticGroupIndex.class)
.build(GroupIndex.Factory.class));
+ install(
+ new FactoryModuleBuilder()
+ .implement(ProjectIndex.class, ElasticProjectIndex.class)
+ .build(ProjectIndex.Factory.class));
install(new IndexModule(threads));
if (singleVersions == null) {
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
new file mode 100644
index 0000000..780f023
--- /dev/null
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
@@ -0,0 +1,215 @@
+// 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.elasticsearch;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
+import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.Schema;
+import com.google.gerrit.index.query.DataSource;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.index.IndexUtils;
+import com.google.gerrit.server.index.project.ProjectField;
+import com.google.gerrit.server.index.project.ProjectIndex;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
+import io.searchbox.client.JestResult;
+import io.searchbox.core.Bulk;
+import io.searchbox.core.Bulk.Builder;
+import io.searchbox.core.Search;
+import io.searchbox.core.search.sort.Sort;
+import io.searchbox.core.search.sort.Sort.Sorting;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.eclipse.jgit.lib.Config;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, ProjectData>
+ implements ProjectIndex {
+ static class ProjectMapping {
+ MappingProperties projects;
+
+ ProjectMapping(Schema<ProjectData> schema) {
+ this.projects = ElasticMapping.createMapping(schema);
+ }
+ }
+
+ static final String PROJECTS = "projects";
+ static final String PROJECTS_PREFIX = PROJECTS + "_";
+
+ private static final Logger log = LoggerFactory.getLogger(ElasticProjectIndex.class);
+
+ private final ProjectMapping mapping;
+ private final Provider<ProjectCache> projectCache;
+
+ @Inject
+ ElasticProjectIndex(
+ @GerritServerConfig Config cfg,
+ SitePaths sitePaths,
+ Provider<ProjectCache> projectCache,
+ JestClientBuilder clientBuilder,
+ @Assisted Schema<ProjectData> schema) {
+ super(cfg, sitePaths, schema, clientBuilder, PROJECTS_PREFIX);
+ this.projectCache = projectCache;
+ this.mapping = new ProjectMapping(schema);
+ }
+
+ @Override
+ public void replace(ProjectData projectState) throws IOException {
+ Bulk bulk =
+ new Bulk.Builder()
+ .defaultIndex(indexName)
+ .defaultType(PROJECTS)
+ .addAction(insert(PROJECTS, projectState))
+ .refresh(true)
+ .build();
+ JestResult result = client.execute(bulk);
+ if (!result.isSucceeded()) {
+ throw new IOException(
+ String.format(
+ "Failed to replace project %s in index %s: %s",
+ projectState.getProject().getName(), indexName, result.getErrorMessage()));
+ }
+ }
+
+ @Override
+ public DataSource<ProjectData> getSource(Predicate<ProjectData> p, QueryOptions opts)
+ throws QueryParseException {
+ return new QuerySource(p, opts);
+ }
+
+ @Override
+ protected Builder addActions(Builder builder, Project.NameKey nameKey) {
+ return builder.addAction(delete(PROJECTS, nameKey));
+ }
+
+ @Override
+ protected String getMappings() {
+ ImmutableMap<String, ProjectMapping> mappings = ImmutableMap.of("mappings", mapping);
+ return gson.toJson(mappings);
+ }
+
+ @Override
+ protected String getId(ProjectData projectState) {
+ return projectState.getProject().getName();
+ }
+
+ private class QuerySource implements DataSource<ProjectData> {
+ private final Search search;
+ private final Set<String> fields;
+
+ QuerySource(Predicate<ProjectData> p, QueryOptions opts) throws QueryParseException {
+ QueryBuilder qb = queryBuilder.toQueryBuilder(p);
+ fields = IndexUtils.projectFields(opts);
+ SearchSourceBuilder searchSource =
+ new SearchSourceBuilder()
+ .query(qb)
+ .from(opts.start())
+ .size(opts.limit())
+ .fields(Lists.newArrayList(fields));
+
+ Sort sort = new Sort(ProjectField.NAME.getName(), Sorting.ASC);
+ sort.setIgnoreUnmapped();
+
+ search =
+ new Search.Builder(searchSource.toString())
+ .addType(PROJECTS)
+ .addIndex(indexName)
+ .addSort(ImmutableList.of(sort))
+ .build();
+ }
+
+ @Override
+ public int getCardinality() {
+ return 10;
+ }
+
+ @Override
+ public ResultSet<ProjectData> read() throws OrmException {
+ try {
+ List<ProjectData> results = Collections.emptyList();
+ JestResult result = client.execute(search);
+ if (result.isSucceeded()) {
+ JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
+ if (obj.get("hits") != null) {
+ JsonArray json = obj.getAsJsonArray("hits");
+ results = Lists.newArrayListWithCapacity(json.size());
+ for (int i = 0; i < json.size(); i++) {
+ results.add(toProjectData(json.get(i)));
+ }
+ }
+ } else {
+ log.error(result.getErrorMessage());
+ }
+ final List<ProjectData> r = Collections.unmodifiableList(results);
+ return new ResultSet<ProjectData>() {
+ @Override
+ public Iterator<ProjectData> iterator() {
+ return r.iterator();
+ }
+
+ @Override
+ public List<ProjectData> toList() {
+ return r;
+ }
+
+ @Override
+ public void close() {
+ // Do nothing.
+ }
+ };
+ } catch (IOException e) {
+ throw new OrmException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return search.toString();
+ }
+
+ private ProjectData toProjectData(JsonElement json) {
+ JsonElement source = json.getAsJsonObject().get("_source");
+ if (source == null) {
+ source = json.getAsJsonObject().get("fields");
+ }
+
+ Project.NameKey nameKey =
+ new Project.NameKey(
+ source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString());
+ return projectCache.get().get(nameKey).toProjectData();
+ }
+ }
+}
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticQueryProjectsTest.java b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticQueryProjectsTest.java
new file mode 100644
index 0000000..4af53e3
--- /dev/null
+++ b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticQueryProjectsTest.java
@@ -0,0 +1,65 @@
+// 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.elasticsearch;
+
+import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo;
+import com.google.gerrit.server.query.project.AbstractQueryProjectsTest;
+import com.google.gerrit.testutil.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class ElasticQueryProjectsTest extends AbstractQueryProjectsTest {
+ private static ElasticNodeInfo nodeInfo;
+
+ @BeforeClass
+ public static void startIndexService() throws InterruptedException, ExecutionException {
+ if (nodeInfo != null) {
+ // do not start Elasticsearch twice
+ return;
+ }
+ nodeInfo = ElasticTestUtils.startElasticsearchNode();
+ ElasticTestUtils.createAllIndexes(nodeInfo);
+ }
+
+ @AfterClass
+ public static void stopElasticsearchServer() {
+ if (nodeInfo != null) {
+ nodeInfo.node.close();
+ nodeInfo.elasticDir.delete();
+ nodeInfo = null;
+ }
+ }
+
+ @After
+ public void cleanupIndex() {
+ if (nodeInfo != null) {
+ ElasticTestUtils.deleteAllIndexes(nodeInfo);
+ ElasticTestUtils.createAllIndexes(nodeInfo);
+ }
+ }
+
+ @Override
+ protected Injector createInjector() {
+ Config elasticsearchConfig = new Config(config);
+ InMemoryModule.setDefaults(elasticsearchConfig);
+ ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port);
+ return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ }
+}
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
index fac10eb..c37a8ec 100644
--- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
+++ b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
@@ -20,12 +20,14 @@
import static com.google.gerrit.elasticsearch.ElasticChangeIndex.CLOSED_CHANGES;
import static com.google.gerrit.elasticsearch.ElasticChangeIndex.OPEN_CHANGES;
import static com.google.gerrit.elasticsearch.ElasticGroupIndex.GROUPS_PREFIX;
+import static com.google.gerrit.elasticsearch.ElasticProjectIndex.PROJECTS_PREFIX;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.gerrit.elasticsearch.ElasticAccountIndex.AccountMapping;
import com.google.gerrit.elasticsearch.ElasticChangeIndex.ChangeMapping;
import com.google.gerrit.elasticsearch.ElasticGroupIndex.GroupMapping;
+import com.google.gerrit.elasticsearch.ElasticProjectIndex.ProjectMapping;
import com.google.gerrit.index.Schema;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.group.InternalGroup;
@@ -33,6 +35,8 @@
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
+import com.google.gerrit.server.index.project.ProjectSchemaDefinitions;
+import com.google.gerrit.server.project.ProjectData;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
@@ -157,6 +161,18 @@
.addMapping(ElasticGroupIndex.GROUPS, gson.toJson(groupMapping))
.execute()
.actionGet();
+
+ Schema<ProjectData> projectSchema = ProjectSchemaDefinitions.INSTANCE.getLatest();
+ ProjectMapping projectMapping = new ProjectMapping(projectSchema);
+ nodeInfo
+ .node
+ .client()
+ .admin()
+ .indices()
+ .prepareCreate(String.format("%s%04d", PROJECTS_PREFIX, projectSchema.getVersion()))
+ .addMapping(ElasticProjectIndex.PROJECTS, gson.toJson(projectMapping))
+ .execute()
+ .actionGet();
}
private static String getHttpPort(Node node) throws InterruptedException, ExecutionException {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/DescriptionInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/DescriptionInput.java
index 322b076..672602d 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/DescriptionInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/DescriptionInput.java
@@ -14,9 +14,6 @@
package com.google.gerrit.extensions.api.projects;
-import com.google.gerrit.extensions.restapi.DefaultInput;
-
-public class DescriptionInput {
- @DefaultInput public String description;
+public class DescriptionInput extends com.google.gerrit.extensions.common.DescriptionInput {
public String commitMessage;
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java
index e4a659c..02cce3a 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java
@@ -58,6 +58,24 @@
ListRequest list();
+ /**
+ * Query projects.
+ *
+ * <p>Example code: {@code query().withQuery("name:project").get()}
+ *
+ * @return API for setting parameters and getting result.
+ */
+ QueryRequest query();
+
+ /**
+ * Query projects.
+ *
+ * <p>Shortcut API for {@code query().withQuery(String)}.
+ *
+ * @see #query()
+ */
+ QueryRequest query(String query);
+
abstract class ListRequest {
public enum FilterType {
CODE,
@@ -172,6 +190,56 @@
}
/**
+ * API for setting parameters and getting result. Used for {@code query()}.
+ *
+ * @see #query()
+ */
+ abstract class QueryRequest {
+ private String query;
+ private int limit;
+ private int start;
+
+ /** Execute query and returns the matched projects as list. */
+ public abstract List<ProjectInfo> get() throws RestApiException;
+
+ /**
+ * Set query.
+ *
+ * @param query needs to be in human-readable form.
+ */
+ public QueryRequest withQuery(String query) {
+ this.query = query;
+ return this;
+ }
+
+ /**
+ * Set limit for returned list of projects. Optional; server-default is used when not provided.
+ */
+ public QueryRequest withLimit(int limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ /** Set number of projects to skip. Optional; no projects are skipped when not provided. */
+ public QueryRequest withStart(int start) {
+ this.start = start;
+ return this;
+ }
+
+ public String getQuery() {
+ return query;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ public int getStart() {
+ return start;
+ }
+ }
+
+ /**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
*/
@@ -195,5 +263,15 @@
public ListRequest list() {
throw new NotImplementedException();
}
+
+ @Override
+ public QueryRequest query() {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public QueryRequest query(String query) {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/DescriptionInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/DescriptionInput.java
new file mode 100644
index 0000000..c0733dc
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/DescriptionInput.java
@@ -0,0 +1,21 @@
+// 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.extensions.common;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+public class DescriptionInput {
+ @DefaultInput public String description;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GpgKeysInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GpgKeysInput.java
new file mode 100644
index 0000000..95eb1c4
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GpgKeysInput.java
@@ -0,0 +1,22 @@
+// 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.extensions.common;
+
+import java.util.List;
+
+public class GpgKeysInput {
+ public List<String> add;
+ public List<String> delete;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/HttpPasswordInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/HttpPasswordInput.java
new file mode 100644
index 0000000..246c7cf
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/HttpPasswordInput.java
@@ -0,0 +1,20 @@
+// 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.extensions.common;
+
+public class HttpPasswordInput {
+ public String httpPassword;
+ public boolean generate;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/Input.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/Input.java
new file mode 100644
index 0000000..68f864c
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/Input.java
@@ -0,0 +1,20 @@
+// 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.extensions.common;
+
+/** A generic empty input. */
+public class Input {
+ public Input() {}
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/NameInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/NameInput.java
new file mode 100644
index 0000000..463eee1
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/NameInput.java
@@ -0,0 +1,21 @@
+// 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.extensions.common;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+public class NameInput {
+ @DefaultInput public String name;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/OwnerInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/OwnerInput.java
new file mode 100644
index 0000000..1272a5c
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/OwnerInput.java
@@ -0,0 +1,21 @@
+// 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.extensions.common;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+public class OwnerInput {
+ @DefaultInput public String owner;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/SshKeyInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/SshKeyInput.java
new file mode 100644
index 0000000..e04ea23
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/SshKeyInput.java
@@ -0,0 +1,21 @@
+// 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.extensions.common;
+
+import com.google.gerrit.extensions.restapi.RawInput;
+
+public class SshKeyInput {
+ public RawInput raw;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TopicInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TopicInput.java
new file mode 100644
index 0000000..889efd4
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TopicInput.java
@@ -0,0 +1,21 @@
+// 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.extensions.common;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+public class TopicInput {
+ @DefaultInput public String topic;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/UsernameInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/UsernameInput.java
new file mode 100644
index 0000000..baff84b
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/UsernameInput.java
@@ -0,0 +1,21 @@
+// 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.extensions.common;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+public class UsernameInput {
+ @DefaultInput public String username;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectIndexedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectIndexedListener.java
new file mode 100644
index 0000000..93a610b
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectIndexedListener.java
@@ -0,0 +1,28 @@
+// 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.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/** Notified whenever a project is indexed */
+@ExtensionPoint
+public interface ProjectIndexedListener {
+ /**
+ * Invoked when a project is indexed
+ *
+ * @param project name of the project
+ */
+ void onProjectIndexed(String project);
+}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
index ba79a6f..a34243f 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
import com.google.gerrit.extensions.common.GpgKeyInfo;
+import com.google.gerrit.extensions.common.GpgKeysInput;
import com.google.gerrit.extensions.common.PushCertificateInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -29,6 +30,7 @@
import com.google.gerrit.server.api.accounts.GpgApiAdapter;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -38,15 +40,15 @@
import org.eclipse.jgit.transport.PushCertificateParser;
public class GpgApiAdapterImpl implements GpgApiAdapter {
- private final PostGpgKeys postGpgKeys;
- private final GpgKeys gpgKeys;
+ private final Provider<PostGpgKeys> postGpgKeys;
+ private final Provider<GpgKeys> gpgKeys;
private final GpgKeyApiImpl.Factory gpgKeyApiFactory;
private final GerritPushCertificateChecker.Factory pushCertCheckerFactory;
@Inject
GpgApiAdapterImpl(
- PostGpgKeys postGpgKeys,
- GpgKeys gpgKeys,
+ Provider<PostGpgKeys> postGpgKeys,
+ Provider<GpgKeys> gpgKeys,
GpgKeyApiImpl.Factory gpgKeyApiFactory,
GerritPushCertificateChecker.Factory pushCertCheckerFactory) {
this.postGpgKeys = postGpgKeys;
@@ -64,7 +66,7 @@
public Map<String, GpgKeyInfo> listGpgKeys(AccountResource account)
throws RestApiException, GpgException {
try {
- return gpgKeys.list().apply(account);
+ return gpgKeys.get().list().apply(account);
} catch (OrmException | PGPException | IOException e) {
throw new GpgException(e);
}
@@ -74,11 +76,11 @@
public Map<String, GpgKeyInfo> putGpgKeys(
AccountResource account, List<String> add, List<String> delete)
throws RestApiException, GpgException {
- PostGpgKeys.Input in = new PostGpgKeys.Input();
+ GpgKeysInput in = new GpgKeysInput();
in.add = add;
in.delete = delete;
try {
- return postGpgKeys.apply(account, in);
+ return postGpgKeys.get().apply(account, in);
} catch (PGPException | OrmException | IOException | ConfigInvalidException e) {
throw new GpgException(e);
}
@@ -88,7 +90,7 @@
public GpgKeyApi gpgKey(AccountResource account, IdString idStr)
throws RestApiException, GpgException {
try {
- return gpgKeyApiFactory.create(gpgKeys.parse(account, idStr));
+ return gpgKeyApiFactory.create(gpgKeys.get().parse(account, idStr));
} catch (PGPException | OrmException | IOException e) {
throw new GpgException(e);
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
index 14a4c6d..25b472d 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
import com.google.gerrit.extensions.common.GpgKeyInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.gpg.server.DeleteGpgKey;
import com.google.gerrit.gpg.server.GpgKey;
@@ -55,7 +56,7 @@
@Override
public void delete() throws RestApiException {
try {
- delete.apply(rsrc, new DeleteGpgKey.Input());
+ delete.apply(rsrc, new Input());
} catch (PGPException | OrmException | IOException | ConfigInvalidException e) {
throw new RestApiException("Cannot delete GPG key", e);
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
index baf5a58..b9d89ee 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -18,11 +18,11 @@
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY;
import com.google.common.io.BaseEncoding;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.gpg.PublicKeyStore;
-import com.google.gerrit.gpg.server.DeleteGpgKey.Input;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
@@ -38,7 +38,6 @@
import org.eclipse.jgit.lib.RefUpdate;
public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
- public static class Input {}
private final Provider<PersonIdent> serverIdent;
private final Provider<PublicKeyStore> storeProvider;
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 7d1aceed..b2383ca 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -29,6 +29,7 @@
import com.google.common.io.BaseEncoding;
import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.extensions.common.GpgKeyInfo;
+import com.google.gerrit.extensions.common.GpgKeysInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -38,7 +39,6 @@
import com.google.gerrit.gpg.GerritPublicKeyChecker;
import com.google.gerrit.gpg.PublicKeyChecker;
import com.google.gerrit.gpg.PublicKeyStore;
-import com.google.gerrit.gpg.server.PostGpgKeys.Input;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -75,12 +75,7 @@
import org.slf4j.LoggerFactory;
@Singleton
-public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
- public static class Input {
- public List<String> add;
- public List<String> delete;
- }
-
+public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Provider<PersonIdent> serverIdent;
private final Provider<CurrentUser> self;
@@ -112,7 +107,7 @@
}
@Override
- public Map<String, GpgKeyInfo> apply(AccountResource rsrc, Input input)
+ public Map<String, GpgKeyInfo> apply(AccountResource rsrc, GpgKeysInput input)
throws ResourceNotFoundException, BadRequestException, ResourceConflictException,
PGPException, OrmException, IOException, ConfigInvalidException {
GpgKeys.checkVisible(self, rsrc);
@@ -148,7 +143,8 @@
}
}
- private Set<Fingerprint> readKeysToRemove(Input input, Collection<ExternalId> existingExtIds) {
+ private Set<Fingerprint> readKeysToRemove(
+ GpgKeysInput input, Collection<ExternalId> existingExtIds) {
if (input.delete == null || input.delete.isEmpty()) {
return ImmutableSet.of();
}
@@ -163,7 +159,7 @@
return fingerprints;
}
- private List<PGPPublicKeyRing> readKeysToAdd(Input input, Set<Fingerprint> toRemove)
+ private List<PGPPublicKeyRing> readKeysToAdd(GpgKeysInput input, Set<Fingerprint> toRemove)
throws BadRequestException, IOException {
if (input.add == null || input.add.isEmpty()) {
return ImmutableList.of();
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
index 7c62ed7..a0c4aa6 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
@@ -14,9 +14,6 @@
package com.google.gerrit.client;
-import static com.google.gerrit.client.CommonConstants.C;
-import static com.google.gerrit.client.CommonMessages.M;
-
import java.util.Date;
/**
@@ -24,6 +21,9 @@
* defined by {@code git log --relative-date}.
*/
public class RelativeDateFormatter {
+ private static CommonConstants constants;
+ private static CommonMessages messages;
+
static final long SECOND_IN_MILLIS = 1000;
static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
@@ -32,6 +32,19 @@
static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+ static void setConstants(CommonConstants c, CommonMessages m) {
+ constants = c;
+ messages = m;
+ }
+
+ private static CommonConstants c() {
+ return constants != null ? constants : CommonConstants.C;
+ }
+
+ private static CommonMessages m() {
+ return messages != null ? messages : CommonMessages.M;
+ }
+
/**
* @param when {@link Date} to format
* @return age of given {@link Date} compared to now formatted in the same relative format as
@@ -42,81 +55,85 @@
// shouldn't happen in a perfect world
if (ageMillis < 0) {
- return C.inTheFuture();
+ return c().inTheFuture();
}
// seconds
if (ageMillis < upperLimit(MINUTE_IN_MILLIS)) {
long seconds = round(ageMillis, SECOND_IN_MILLIS);
if (seconds == 1) {
- return C.oneSecondAgo();
+ return c().oneSecondAgo();
}
- return M.secondsAgo(seconds);
+ return m().secondsAgo(seconds);
}
// minutes
if (ageMillis < upperLimit(HOUR_IN_MILLIS)) {
long minutes = round(ageMillis, MINUTE_IN_MILLIS);
if (minutes == 1) {
- return C.oneMinuteAgo();
+ return c().oneMinuteAgo();
}
- return M.minutesAgo(minutes);
+ return m().minutesAgo(minutes);
}
// hours
if (ageMillis < upperLimit(DAY_IN_MILLIS)) {
long hours = round(ageMillis, HOUR_IN_MILLIS);
if (hours == 1) {
- return C.oneHourAgo();
+ return c().oneHourAgo();
}
- return M.hoursAgo(hours);
+ return m().hoursAgo(hours);
}
// up to 14 days use days
if (ageMillis < 14 * DAY_IN_MILLIS) {
long days = round(ageMillis, DAY_IN_MILLIS);
if (days == 1) {
- return C.oneDayAgo();
+ return c().oneDayAgo();
}
- return M.daysAgo(days);
+ return m().daysAgo(days);
}
// up to 10 weeks use weeks
if (ageMillis < 10 * WEEK_IN_MILLIS) {
long weeks = round(ageMillis, WEEK_IN_MILLIS);
if (weeks == 1) {
- return C.oneWeekAgo();
+ return c().oneWeekAgo();
}
- return M.weeksAgo(weeks);
+ return m().weeksAgo(weeks);
}
// months
if (ageMillis < YEAR_IN_MILLIS) {
long months = round(ageMillis, MONTH_IN_MILLIS);
if (months == 1) {
- return C.oneMonthAgo();
+ return c().oneMonthAgo();
}
- return M.monthsAgo(months);
+ return m().monthsAgo(months);
}
// up to 5 years use "year, months" rounded to months
if (ageMillis < 5 * YEAR_IN_MILLIS) {
long years = ageMillis / YEAR_IN_MILLIS;
- String yearLabel = (years > 1) ? C.years() : C.year();
+ String yearLabel = (years > 1) ? c().years() : c().year();
long months = round(ageMillis % YEAR_IN_MILLIS, MONTH_IN_MILLIS);
- String monthLabel = (months > 1) ? C.months() : (months == 1 ? C.month() : "");
+ String monthLabel = (months > 1) ? c().months() : (months == 1 ? c().month() : "");
if (months == 0) {
- return M.years0MonthsAgo(years, yearLabel);
+ return m().years0MonthsAgo(years, yearLabel);
}
- return M.yearsMonthsAgo(years, yearLabel, months, monthLabel);
+ if (months == 12) {
+ years++;
+ return m().years0MonthsAgo(years, yearLabel);
+ }
+ return m().yearsMonthsAgo(years, yearLabel, months, monthLabel);
}
// years
long years = round(ageMillis, YEAR_IN_MILLIS);
if (years == 1) {
- return C.oneYearAgo();
+ return c().oneYearAgo();
}
- return M.yearsAgo(years);
+ return m().yearsAgo(years);
}
private static long upperLimit(long unit) {
diff --git a/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java b/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java
index 937fc96..5180410 100644
--- a/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java
+++ b/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java
@@ -22,11 +22,23 @@
import static org.junit.Assert.assertEquals;
import java.util.Date;
-import org.eclipse.jgit.util.RelativeDateFormatter;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
import org.junit.Test;
public class RelativeDateFormatterTest {
+ @BeforeClass
+ public static void setConstants() {
+ Constants c = new Constants();
+ RelativeDateFormatter.setConstants(c, c);
+ }
+
+ @AfterClass
+ public static void unsetConstants() {
+ RelativeDateFormatter.setConstants(null, null);
+ }
+
private static void assertFormat(long ageFromNow, long timeUnit, String expectedFormat) {
Date d = new Date(System.currentTimeMillis() - ageFromNow * timeUnit);
String s = RelativeDateFormatter.format(d);
@@ -41,7 +53,7 @@
@Test
public void formatSeconds() {
- assertFormat(1, SECOND_IN_MILLIS, "1 seconds ago");
+ assertFormat(1, SECOND_IN_MILLIS, "1 second ago");
assertFormat(89, SECOND_IN_MILLIS, "89 seconds ago");
}
@@ -85,7 +97,7 @@
assertFormat(380, DAY_IN_MILLIS, "1 year, 1 month ago");
assertFormat(410, DAY_IN_MILLIS, "1 year, 2 months ago");
assertFormat(2, YEAR_IN_MILLIS, "2 years ago");
- assertFormat(1824, DAY_IN_MILLIS, "4 years, 12 months ago");
+ assertFormat(1824, DAY_IN_MILLIS, "5 years ago");
}
@Test
@@ -93,4 +105,111 @@
assertFormat(5, YEAR_IN_MILLIS, "5 years ago");
assertFormat(60, YEAR_IN_MILLIS, "60 years ago");
}
+
+ private static class Constants implements CommonConstants, CommonMessages {
+ @Override
+ public String inTheFuture() {
+ return "in the future";
+ }
+
+ @Override
+ public String month() {
+ return "month";
+ }
+
+ @Override
+ public String months() {
+ return "months";
+ }
+
+ @Override
+ public String year() {
+ return "year";
+ }
+
+ @Override
+ public String years() {
+ return "years";
+ }
+
+ @Override
+ public String oneSecondAgo() {
+ return "1 second ago";
+ }
+
+ @Override
+ public String oneMinuteAgo() {
+ return "1 minute ago";
+ }
+
+ @Override
+ public String oneHourAgo() {
+ return "1 hour ago";
+ }
+
+ @Override
+ public String oneDayAgo() {
+ return "1 day ago";
+ }
+
+ @Override
+ public String oneWeekAgo() {
+ return "1 week ago";
+ }
+
+ @Override
+ public String oneMonthAgo() {
+ return "1 month ago";
+ }
+
+ @Override
+ public String oneYearAgo() {
+ return "1 year ago";
+ }
+
+ @Override
+ public String secondsAgo(long seconds) {
+ return seconds + " seconds ago";
+ }
+
+ @Override
+ public String minutesAgo(long minutes) {
+ return minutes + " minutes ago";
+ }
+
+ @Override
+ public String hoursAgo(long hours) {
+ return hours + " hours ago";
+ }
+
+ @Override
+ public String daysAgo(long days) {
+ return days + " days ago";
+ }
+
+ @Override
+ public String weeksAgo(long weeks) {
+ return weeks + " weeks ago";
+ }
+
+ @Override
+ public String monthsAgo(long months) {
+ return months + " months ago";
+ }
+
+ @Override
+ public String yearsAgo(long years) {
+ return years + " years ago";
+ }
+
+ @Override
+ public String years0MonthsAgo(long years, String yearLabel) {
+ return years + " " + yearLabel + " ago";
+ }
+
+ @Override
+ public String yearsMonthsAgo(long years, String yearLabel, long months, String monthLabel) {
+ return years + " " + yearLabel + ", " + months + " " + monthLabel + " ago";
+ }
+ }
}
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index c3b522a..b22a2a4 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -21,7 +21,7 @@
* @param staticResourcePath
* @param? versionInfo
*/
-{template .Index kind="html"}
+{template .Index}
<!DOCTYPE html>{\n}
<html lang="en">{\n}
<meta charset="utf-8">{\n}
diff --git a/gerrit-index/BUILD b/gerrit-index/BUILD
index 41eed60..11c4f08 100644
--- a/gerrit-index/BUILD
+++ b/gerrit-index/BUILD
@@ -1,7 +1,10 @@
load("//tools/bzl:genrule2.bzl", "genrule2")
load("//tools/bzl:junit.bzl", "junit_tests")
-QUERY_PARSE_EXCEPTION_SRCS = ["src/main/java/com/google/gerrit/index/query/QueryParseException.java"]
+QUERY_PARSE_EXCEPTION_SRCS = [
+ "src/main/java/com/google/gerrit/index/query/QueryParseException.java",
+ "src/main/java/com/google/gerrit/index/query/QueryRequiresAuthException.java",
+]
java_library(
name = "query_exception",
diff --git a/gerrit-index/src/main/java/com/google/gerrit/index/IndexConfig.java b/gerrit-index/src/main/java/com/google/gerrit/index/IndexConfig.java
index b53b59b..b5b36f1 100644
--- a/gerrit-index/src/main/java/com/google/gerrit/index/IndexConfig.java
+++ b/gerrit-index/src/main/java/com/google/gerrit/index/IndexConfig.java
@@ -61,15 +61,15 @@
public abstract static class Builder {
public abstract Builder maxLimit(int maxLimit);
- abstract int maxLimit();
+ public abstract int maxLimit();
public abstract Builder maxPages(int maxPages);
- abstract int maxPages();
+ public abstract int maxPages();
public abstract Builder maxTerms(int maxTerms);
- abstract int maxTerms();
+ public abstract int maxTerms();
public abstract Builder separateChangeSubIndexes(boolean separate);
diff --git a/gerrit-index/src/main/java/com/google/gerrit/index/query/QueryRequiresAuthException.java b/gerrit-index/src/main/java/com/google/gerrit/index/query/QueryRequiresAuthException.java
new file mode 100644
index 0000000..67c159e
--- /dev/null
+++ b/gerrit-index/src/main/java/com/google/gerrit/index/query/QueryRequiresAuthException.java
@@ -0,0 +1,32 @@
+// 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.index.query;
+
+/**
+ * Exception thrown when a search query is invalid.
+ *
+ * <p><b>NOTE:</b> the message is visible to end users.
+ */
+public class QueryRequiresAuthException extends QueryParseException {
+ private static final long serialVersionUID = 1L;
+
+ public QueryRequiresAuthException(String message) {
+ super(message);
+ }
+
+ public QueryRequiresAuthException(String msg, Throwable why) {
+ super(msg, why);
+ }
+}
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java
index d738540..d5d6360 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java
@@ -27,6 +27,7 @@
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.group.GroupIndex;
+import com.google.gerrit.server.index.project.ProjectIndex;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -85,7 +86,10 @@
new FactoryModuleBuilder()
.implement(GroupIndex.class, LuceneGroupIndex.class)
.build(GroupIndex.Factory.class));
-
+ install(
+ new FactoryModuleBuilder()
+ .implement(ProjectIndex.class, LuceneProjectIndex.class)
+ .build(ProjectIndex.Factory.class));
install(new IndexModule(threads));
if (singleVersions == null) {
install(new MultiVersionModule());
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneProjectIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneProjectIndex.java
new file mode 100644
index 0000000..6354f61
--- /dev/null
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneProjectIndex.java
@@ -0,0 +1,200 @@
+// 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.lucene;
+
+import static com.google.gerrit.server.index.project.ProjectField.NAME;
+
+import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.Schema;
+import com.google.gerrit.index.query.DataSource;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.index.IndexUtils;
+import com.google.gerrit.server.index.project.ProjectIndex;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.SearcherFactory;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.TopFieldDocs;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.RAMDirectory;
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LuceneProjectIndex extends AbstractLuceneIndex<Project.NameKey, ProjectData>
+ implements ProjectIndex {
+ private static final Logger log = LoggerFactory.getLogger(LuceneProjectIndex.class);
+
+ private static final String PROJECTS = "projects";
+
+ private static final String NAME_SORT_FIELD = sortFieldName(NAME);
+
+ private static Term idTerm(ProjectData projectState) {
+ return idTerm(projectState.getProject().getNameKey());
+ }
+
+ private static Term idTerm(Project.NameKey nameKey) {
+ return QueryBuilder.stringTerm(NAME.getName(), nameKey.get());
+ }
+
+ private final GerritIndexWriterConfig indexWriterConfig;
+ private final QueryBuilder<ProjectData> queryBuilder;
+ private final Provider<ProjectCache> projectCache;
+
+ private static Directory dir(Schema<ProjectData> schema, Config cfg, SitePaths sitePaths)
+ throws IOException {
+ if (LuceneIndexModule.isInMemoryTest(cfg)) {
+ return new RAMDirectory();
+ }
+ Path indexDir = LuceneVersionManager.getDir(sitePaths, PROJECTS, schema);
+ return FSDirectory.open(indexDir);
+ }
+
+ @Inject
+ LuceneProjectIndex(
+ @GerritServerConfig Config cfg,
+ SitePaths sitePaths,
+ Provider<ProjectCache> projectCache,
+ @Assisted Schema<ProjectData> schema)
+ throws IOException {
+ super(
+ schema,
+ sitePaths,
+ dir(schema, cfg, sitePaths),
+ PROJECTS,
+ null,
+ new GerritIndexWriterConfig(cfg, PROJECTS),
+ new SearcherFactory());
+ this.projectCache = projectCache;
+
+ indexWriterConfig = new GerritIndexWriterConfig(cfg, PROJECTS);
+ queryBuilder = new QueryBuilder<>(schema, indexWriterConfig.getAnalyzer());
+ }
+
+ @Override
+ public void replace(ProjectData projectState) throws IOException {
+ try {
+ replace(idTerm(projectState), toDocument(projectState)).get();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void delete(Project.NameKey nameKey) throws IOException {
+ try {
+ delete(idTerm(nameKey)).get();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public DataSource<ProjectData> getSource(Predicate<ProjectData> p, QueryOptions opts)
+ throws QueryParseException {
+ return new QuerySource(
+ opts,
+ queryBuilder.toQuery(p),
+ new Sort(new SortField(NAME_SORT_FIELD, SortField.Type.STRING, false)));
+ }
+
+ private class QuerySource implements DataSource<ProjectData> {
+ private final QueryOptions opts;
+ private final Query query;
+ private final Sort sort;
+
+ private QuerySource(QueryOptions opts, Query query, Sort sort) {
+ this.opts = opts;
+ this.query = query;
+ this.sort = sort;
+ }
+
+ @Override
+ public int getCardinality() {
+ return 10;
+ }
+
+ @Override
+ public ResultSet<ProjectData> read() throws OrmException {
+ IndexSearcher searcher = null;
+ try {
+ searcher = acquire();
+ int realLimit = opts.start() + opts.limit();
+ TopFieldDocs docs = searcher.search(query, realLimit, sort);
+ List<ProjectData> result = new ArrayList<>(docs.scoreDocs.length);
+ for (int i = opts.start(); i < docs.scoreDocs.length; i++) {
+ ScoreDoc sd = docs.scoreDocs[i];
+ Document doc = searcher.doc(sd.doc, IndexUtils.projectFields(opts));
+ result.add(toProjectData(doc));
+ }
+ final List<ProjectData> r = Collections.unmodifiableList(result);
+ return new ResultSet<ProjectData>() {
+ @Override
+ public Iterator<ProjectData> iterator() {
+ return r.iterator();
+ }
+
+ @Override
+ public List<ProjectData> toList() {
+ return r;
+ }
+
+ @Override
+ public void close() {
+ // Do nothing.
+ }
+ };
+ } catch (IOException e) {
+ throw new OrmException(e);
+ } finally {
+ if (searcher != null) {
+ try {
+ release(searcher);
+ } catch (IOException e) {
+ log.warn("cannot release Lucene searcher", e);
+ }
+ }
+ }
+ }
+ }
+
+ private ProjectData toProjectData(Document doc) {
+ Project.NameKey nameKey = new Project.NameKey(doc.getField(NAME.getName()).stringValue());
+ return projectCache.get().get(nameKey).toProjectData();
+ }
+}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
index bee9928..d8451d5 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
@@ -32,6 +32,7 @@
import com.google.gerrit.pgm.util.ThreadLimiter;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
@@ -170,6 +171,7 @@
@Override
protected void configure() {
factory(ChangeResource.Factory.class);
+ bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
}
});
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
index feb91e7..ab491f7c 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
@@ -16,11 +16,13 @@
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
import com.google.gerrit.pgm.init.api.InitFlags;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIdReader;
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.File;
@@ -67,7 +69,17 @@
PersonIdent serverIdent = new GerritPersonIdentProvider(flags.cfg).get();
ExternalIdsUpdate.commit(
- repo, rw, ins, rev, noteMap, commitMessage, serverIdent, serverIdent);
+ new Project.NameKey(allUsers),
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ commitMessage,
+ serverIdent,
+ serverIdent,
+ null,
+ GitReferenceUpdated.DISABLED);
}
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java
index 86468c9..9a81c52 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -17,6 +17,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
@@ -31,8 +32,11 @@
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.account.AccountIndexCollection;
+import com.google.gerrit.server.index.group.GroupIndex;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import java.io.IOException;
@@ -55,7 +59,8 @@
private final SequencesOnInit sequencesOnInit;
private final GroupsOnInit groupsOnInit;
private SchemaFactory<ReviewDb> dbFactory;
- private AccountIndexCollection indexCollection;
+ private AccountIndexCollection accountIndexCollection;
+ private GroupIndexCollection groupIndexCollection;
@Inject
InitAdminUser(
@@ -86,8 +91,13 @@
}
@Inject(optional = true)
- void set(AccountIndexCollection indexCollection) {
- this.indexCollection = indexCollection;
+ void set(AccountIndexCollection accountIndexCollection) {
+ this.accountIndexCollection = accountIndexCollection;
+ }
+
+ @Inject(optional = true)
+ void set(GroupIndexCollection groupIndexCollection) {
+ this.groupIndexCollection = groupIndexCollection;
}
@Override
@@ -138,9 +148,15 @@
Collections.singleton(adminGroup.getGroupUUID()),
extIds,
new HashMap<>());
- for (AccountIndex accountIndex : indexCollection.getWriteIndexes()) {
+ for (AccountIndex accountIndex : accountIndexCollection.getWriteIndexes()) {
accountIndex.replace(as);
}
+
+ InternalGroup adminInternalGroup =
+ InternalGroup.create(adminGroup, ImmutableSet.of(id), ImmutableSet.of());
+ for (GroupIndex groupIndex : groupIndexCollection.getWriteIndexes()) {
+ groupIndex.replace(adminInternalGroup);
+ }
}
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
index bfad889..e1cef62 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
@@ -17,6 +17,7 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.RepoSequence;
import com.google.gwtorm.server.OrmException;
@@ -40,6 +41,7 @@
RepoSequence accountSeq =
new RepoSequence(
repoManager,
+ GitReferenceUpdated.DISABLED,
new Project.NameKey(allUsersName.get()),
Sequences.NAME_ACCOUNTS,
accountSeed,
diff --git a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh
index d331347..20edbd3 100755
--- a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh
+++ b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh
@@ -289,7 +289,11 @@
GERRIT_FDS=`get_config --int core.packedGitOpenFiles`
test -z "$GERRIT_FDS" && GERRIT_FDS=128
-GERRIT_FDS=`expr $GERRIT_FDS + $GERRIT_FDS`
+FDS_MULTIPLIER=2
+USE_LFS=`get_config --get lfs.plugin`
+test -n "$USE_LFS" && FDS_MULTIPLIER=3
+
+GERRIT_FDS=`expr $FDS_MULTIPLIER \* $GERRIT_FDS`
test $GERRIT_FDS -lt 1024 && GERRIT_FDS=1024
GERRIT_STARTUP_TIMEOUT=`get_config --get container.startupTimeout`
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java b/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java
index 28fde9e..930f3f3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java
@@ -27,6 +27,7 @@
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.RepoSequence;
@@ -64,6 +65,7 @@
Provider<ReviewDb> db,
NotesMigration migration,
GitRepositoryManager repoManager,
+ GitReferenceUpdated gitRefUpdated,
AllProjectsName allProjects,
AllUsersName allUsers,
MetricMaker metrics) {
@@ -74,6 +76,7 @@
accountSeq =
new RepoSequence(
repoManager,
+ gitRefUpdated,
allUsers,
NAME_ACCOUNTS,
() -> ReviewDb.FIRST_ACCOUNT_ID,
@@ -84,7 +87,8 @@
RepoSequence.Seed changeSeed = () -> db.get().nextChangeId() + gap;
int changeBatchSize = cfg.getInt("noteDb", "changes", "sequenceBatchSize", 20);
changeSeq =
- new RepoSequence(repoManager, allProjects, NAME_CHANGES, changeSeed, changeBatchSize);
+ new RepoSequence(
+ repoManager, gitRefUpdated, allProjects, NAME_CHANGES, changeSeed, changeBatchSize);
nextIdLatency =
metrics.newTimer(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
index a8cc0f4..12bd8ff 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -37,6 +37,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexer;
@@ -163,6 +164,7 @@
ImmutableSortedSet.of(DEFAULT_LABEL);
private final GitRepositoryManager repoManager;
+ private final GitReferenceUpdated gitRefUpdated;
private final AllUsersName allUsers;
private final Provider<ReviewDb> dbProvider;
private final PersonIdent serverIdent;
@@ -172,12 +174,14 @@
@Inject
StarredChangesUtil(
GitRepositoryManager repoManager,
+ GitReferenceUpdated gitRefUpdated,
AllUsersName allUsers,
Provider<ReviewDb> dbProvider,
@GerritPersonIdent PersonIdent serverIdent,
ChangeIndexer indexer,
Provider<InternalChangeQuery> queryProvider) {
this.repoManager = repoManager;
+ this.gitRefUpdated = gitRefUpdated;
this.allUsers = allUsers;
this.dbProvider = dbProvider;
this.serverIdent = serverIdent;
@@ -403,18 +407,8 @@
throw new MutuallyExclusiveLabelsException(DEFAULT_LABEL, IGNORE_LABEL);
}
- Set<Integer> reviewedPatchSets =
- labels
- .stream()
- .filter(l -> l.startsWith(REVIEWED_LABEL))
- .map(l -> Integer.valueOf(l.substring(REVIEWED_LABEL.length() + 1)))
- .collect(toSet());
- Set<Integer> unreviewedPatchSets =
- labels
- .stream()
- .filter(l -> l.startsWith(UNREVIEWED_LABEL))
- .map(l -> Integer.valueOf(l.substring(UNREVIEWED_LABEL.length() + 1)))
- .collect(toSet());
+ Set<Integer> reviewedPatchSets = getStarredPatchSets(labels, REVIEWED_LABEL);
+ Set<Integer> unreviewedPatchSets = getStarredPatchSets(labels, UNREVIEWED_LABEL);
Optional<Integer> ps =
Sets.intersection(reviewedPatchSets, unreviewedPatchSets).stream().findFirst();
if (ps.isPresent()) {
@@ -423,6 +417,15 @@
}
}
+ public static Set<Integer> getStarredPatchSets(Set<String> labels, String label) {
+ return labels
+ .stream()
+ .filter(l -> l.startsWith(label))
+ .filter(l -> Ints.tryParse(l.substring(label.length() + 1)) != null)
+ .map(l -> Integer.valueOf(l.substring(label.length() + 1)))
+ .collect(toSet());
+ }
+
private static void validateLabels(Collection<String> labels) throws InvalidLabelsException {
if (labels == null) {
return;
@@ -455,6 +458,7 @@
case FORCED:
case NO_CHANGE:
case FAST_FORWARD:
+ gitRefUpdated.fire(allUsers, u, null);
return;
case IO_FAILURE:
case LOCK_FAILURE:
@@ -481,6 +485,7 @@
RefUpdate.Result result = u.delete();
switch (result) {
case FORCED:
+ gitRefUpdated.fire(allUsers, u, null);
return;
case NEW:
case NO_CHANGE:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
index adca6fb..d062842 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -224,7 +224,8 @@
private ImmutableSet<AccountGroup.UUID> getGroupsWithMember(ReviewDb db, Account.Id memberId)
throws OrmException {
Stream<InternalGroup> internalGroupStream;
- if (groupIndexProvider.get().getSchema().hasField(GroupField.MEMBER)) {
+ if (groupIndexProvider.get() != null
+ && groupIndexProvider.get().getSchema().hasField(GroupField.MEMBER)) {
internalGroupStream = groupQueryProvider.get().byMember(memberId).stream();
} else {
internalGroupStream =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
index 1c5495f..79a16e7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
@@ -20,6 +20,7 @@
import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.common.errors.InvalidSshKeyException;
import com.google.gerrit.extensions.common.SshKeyInfo;
+import com.google.gerrit.extensions.common.SshKeyInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RawInput;
@@ -28,7 +29,6 @@
import com.google.gerrit.reviewdb.client.AccountSshKey;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AddSshKey.Input;
import com.google.gerrit.server.mail.send.AddKeySender;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -45,13 +45,9 @@
import org.slf4j.LoggerFactory;
@Singleton
-public class AddSshKey implements RestModifyView<AccountResource, Input> {
+public class AddSshKey implements RestModifyView<AccountResource, SshKeyInput> {
private static final Logger log = LoggerFactory.getLogger(AddSshKey.class);
- public static class Input {
- public RawInput raw;
- }
-
private final Provider<CurrentUser> self;
private final PermissionBackend permissionBackend;
private final VersionedAuthorizedKeys.Accessor authorizedKeys;
@@ -73,7 +69,7 @@
}
@Override
- public Response<SshKeyInfo> apply(AccountResource rsrc, Input input)
+ public Response<SshKeyInfo> apply(AccountResource rsrc, SshKeyInput input)
throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException,
PermissionBackendException {
if (self.get() != rsrc.getUser()) {
@@ -82,10 +78,10 @@
return apply(rsrc.getUser(), input);
}
- public Response<SshKeyInfo> apply(IdentifiedUser user, Input input)
+ public Response<SshKeyInfo> apply(IdentifiedUser user, SshKeyInput input)
throws BadRequestException, IOException, ConfigInvalidException {
if (input == null) {
- input = new Input();
+ input = new SshKeyInput();
}
if (input.raw == null) {
throw new BadRequestException("SSH public key missing");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
index 43669c0..4b3bf39 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
@@ -16,12 +16,12 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
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.server.IdentifiedUser;
-import com.google.gerrit.server.account.DeleteActive.Input;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -32,7 +32,6 @@
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
@Singleton
public class DeleteActive implements RestModifyView<AccountResource, Input> {
- public static class Input {}
private final Provider<IdentifiedUser> self;
private final SetInactiveFlag setInactiveFlag;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
index aec3a14..cccac63 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
@@ -17,6 +17,7 @@
import static java.util.stream.Collectors.toSet;
import com.google.gerrit.extensions.client.AccountFieldName;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -25,7 +26,6 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.DeleteEmail.Input;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -41,7 +41,6 @@
@Singleton
public class DeleteEmail implements RestModifyView<AccountResource.Email, Input> {
- public static class Input {}
private final Provider<CurrentUser> self;
private final Realm realm;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
index f1ecd29..8dec7d9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.DeleteSshKey.Input;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +33,6 @@
@Singleton
public class DeleteSshKey implements RestModifyView<AccountResource.SshKey, Input> {
- public static class Input {}
private final Provider<CurrentUser> self;
private final PermissionBackend permissionBackend;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
index 81996ab..7887929 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -21,6 +21,7 @@
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.group.Groups;
import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.query.group.InternalGroupQuery;
import com.google.gwtorm.server.SchemaFactory;
@@ -147,16 +148,32 @@
}
static class ByIdLoader extends CacheLoader<AccountGroup.Id, Optional<InternalGroup>> {
+ private final SchemaFactory<ReviewDb> schema;
+ private final Groups groups;
+ private final boolean hasGroupIndex;
private final Provider<InternalGroupQuery> groupQueryProvider;
@Inject
- ByIdLoader(Provider<InternalGroupQuery> groupQueryProvider) {
+ ByIdLoader(
+ SchemaFactory<ReviewDb> schema,
+ Groups groups,
+ GroupIndexCollection groupIndexCollection,
+ Provider<InternalGroupQuery> groupQueryProvider) {
+ this.schema = schema;
+ this.groups = groups;
+ this.hasGroupIndex = groupIndexCollection.getSearchIndex() != null;
this.groupQueryProvider = groupQueryProvider;
}
@Override
public Optional<InternalGroup> load(AccountGroup.Id key) throws Exception {
- return groupQueryProvider.get().byId(key);
+ if (hasGroupIndex) {
+ return groupQueryProvider.get().byId(key);
+ }
+
+ try (ReviewDb db = schema.open()) {
+ return groups.getGroup(db, key);
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Index.java
index ecc6b8c..8436d1d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Index.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Index.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.Index.Input;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -29,7 +29,6 @@
@Singleton
public class Index implements RestModifyView<AccountResource, Input> {
- public static class Input {}
private final AccountCache accountCache;
private final PermissionBackend permissionBackend;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
index 7ce2ea8..cbfa172 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
@@ -16,10 +16,10 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.account.PutActive.Input;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -29,7 +29,6 @@
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
@Singleton
public class PutActive implements RestModifyView<AccountResource, Input> {
- public static class Input {}
private final SetInactiveFlag setInactiveFlag;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
index e00f6b3..5005212 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.common.HttpPasswordInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -24,7 +25,6 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.PutHttpPassword.Input;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
@@ -40,12 +40,7 @@
import org.apache.commons.codec.binary.Base64;
import org.eclipse.jgit.errors.ConfigInvalidException;
-public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
- public static class Input {
- public String httpPassword;
- public boolean generate;
- }
-
+public class PutHttpPassword implements RestModifyView<AccountResource, HttpPasswordInput> {
private static final int LEN = 31;
private static final SecureRandom rng;
@@ -75,7 +70,7 @@
}
@Override
- public Response<String> apply(AccountResource rsrc, Input input)
+ public Response<String> apply(AccountResource rsrc, HttpPasswordInput input)
throws AuthException, ResourceNotFoundException, ResourceConflictException, OrmException,
IOException, ConfigInvalidException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
@@ -83,7 +78,7 @@
}
if (input == null) {
- input = new Input();
+ input = new HttpPasswordInput();
}
input.httpPassword = Strings.emptyToNull(input.httpPassword);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
index 7537230..0ac9d1d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
@@ -16,8 +16,8 @@
import com.google.common.base.Strings;
import com.google.gerrit.extensions.client.AccountFieldName;
+import com.google.gerrit.extensions.common.NameInput;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
@@ -25,7 +25,6 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.PutName.Input;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -37,11 +36,7 @@
import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
-public class PutName implements RestModifyView<AccountResource, Input> {
- public static class Input {
- @DefaultInput public String name;
- }
-
+public class PutName implements RestModifyView<AccountResource, NameInput> {
private final Provider<CurrentUser> self;
private final Realm realm;
private final PermissionBackend permissionBackend;
@@ -60,7 +55,7 @@
}
@Override
- public Response<String> apply(AccountResource rsrc, Input input)
+ public Response<String> apply(AccountResource rsrc, NameInput input)
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
IOException, PermissionBackendException, ConfigInvalidException {
if (self.get() != rsrc.getUser()) {
@@ -69,11 +64,11 @@
return apply(rsrc.getUser(), input);
}
- public Response<String> apply(IdentifiedUser user, Input input)
+ public Response<String> apply(IdentifiedUser user, NameInput input)
throws MethodNotAllowedException, ResourceNotFoundException, IOException,
ConfigInvalidException {
if (input == null) {
- input = new Input();
+ input = new NameInput();
}
if (!realm.allowsEdit(AccountFieldName.FULL_NAME)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
index b3f8fc5..5f9ddee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
@@ -21,7 +22,6 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.PutPreferred.Input;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -35,7 +35,6 @@
@Singleton
public class PutPreferred implements RestModifyView<AccountResource.Email, Input> {
- static class Input {}
private final Provider<CurrentUser> self;
private final PermissionBackend permissionBackend;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
index a73bdd9..335b0b4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
@@ -16,14 +16,13 @@
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.extensions.client.AccountFieldName;
+import com.google.gerrit.extensions.common.UsernameInput;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.PutUsername.Input;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -35,11 +34,7 @@
import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
-public class PutUsername implements RestModifyView<AccountResource, Input> {
- public static class Input {
- @DefaultInput public String username;
- }
-
+public class PutUsername implements RestModifyView<AccountResource, UsernameInput> {
private final Provider<CurrentUser> self;
private final ChangeUserName.Factory changeUserNameFactory;
private final PermissionBackend permissionBackend;
@@ -58,7 +53,7 @@
}
@Override
- public String apply(AccountResource rsrc, Input input)
+ public String apply(AccountResource rsrc, UsernameInput input)
throws AuthException, MethodNotAllowedException, UnprocessableEntityException,
ResourceConflictException, OrmException, IOException, ConfigInvalidException,
PermissionBackendException {
@@ -71,7 +66,7 @@
}
if (input == null) {
- input = new Input();
+ input = new UsernameInput();
}
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsBatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsBatchUpdate.java
index e35b0c3..8e5582c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsBatchUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsBatchUpdate.java
@@ -17,6 +17,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -41,6 +42,7 @@
*/
public class ExternalIdsBatchUpdate {
private final GitRepositoryManager repoManager;
+ private final GitReferenceUpdated gitRefUpdated;
private final AllUsersName allUsersName;
private final PersonIdent serverIdent;
private final ExternalIdCache externalIdCache;
@@ -50,10 +52,12 @@
@Inject
public ExternalIdsBatchUpdate(
GitRepositoryManager repoManager,
+ GitReferenceUpdated gitRefUpdated,
AllUsersName allUsersName,
@GerritPersonIdent PersonIdent serverIdent,
ExternalIdCache externalIdCache) {
this.repoManager = repoManager;
+ this.gitRefUpdated = gitRefUpdated;
this.allUsersName = allUsersName;
this.serverIdent = serverIdent;
this.externalIdCache = externalIdCache;
@@ -105,7 +109,17 @@
ObjectId newRev =
ExternalIdsUpdate.commit(
- repo, rw, ins, rev, noteMap, commitMessage, serverIdent, serverIdent);
+ allUsersName,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ commitMessage,
+ serverIdent,
+ serverIdent,
+ null,
+ gitRefUpdated);
externalIdCache.onReplace(rev, newRev, toDelete, toAdd);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
index e4434fb..00dc05a5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
@@ -41,11 +41,13 @@
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LockFailureException;
import com.google.gwtorm.server.OrmDuplicateKeyException;
@@ -111,6 +113,7 @@
private final ExternalIds externalIds;
private final ExternalIdCache externalIdCache;
private final Provider<PersonIdent> serverIdent;
+ private final GitReferenceUpdated gitRefUpdated;
@Inject
public Server(
@@ -120,7 +123,8 @@
MetricMaker metricMaker,
ExternalIds externalIds,
ExternalIdCache externalIdCache,
- @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+ @GerritPersonIdent Provider<PersonIdent> serverIdent,
+ GitReferenceUpdated gitRefUpdated) {
this.repoManager = repoManager;
this.accountCache = accountCache;
this.allUsersName = allUsersName;
@@ -128,12 +132,22 @@
this.externalIds = externalIds;
this.externalIdCache = externalIdCache;
this.serverIdent = serverIdent;
+ this.gitRefUpdated = gitRefUpdated;
}
public ExternalIdsUpdate create() {
PersonIdent i = serverIdent.get();
return new ExternalIdsUpdate(
- repoManager, accountCache, allUsersName, metricMaker, externalIds, externalIdCache, i, i);
+ repoManager,
+ accountCache,
+ allUsersName,
+ metricMaker,
+ externalIds,
+ externalIdCache,
+ i,
+ i,
+ null,
+ gitRefUpdated);
}
}
@@ -154,6 +168,7 @@
private final ExternalIds externalIds;
private final ExternalIdCache externalIdCache;
private final Provider<PersonIdent> serverIdent;
+ private final GitReferenceUpdated gitRefUpdated;
@Inject
public ServerNoReindex(
@@ -162,19 +177,30 @@
MetricMaker metricMaker,
ExternalIds externalIds,
ExternalIdCache externalIdCache,
- @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+ @GerritPersonIdent Provider<PersonIdent> serverIdent,
+ GitReferenceUpdated gitRefUpdated) {
this.repoManager = repoManager;
this.allUsersName = allUsersName;
this.metricMaker = metricMaker;
this.externalIds = externalIds;
this.externalIdCache = externalIdCache;
this.serverIdent = serverIdent;
+ this.gitRefUpdated = gitRefUpdated;
}
public ExternalIdsUpdate create() {
PersonIdent i = serverIdent.get();
return new ExternalIdsUpdate(
- repoManager, null, allUsersName, metricMaker, externalIds, externalIdCache, i, i);
+ repoManager,
+ null,
+ allUsersName,
+ metricMaker,
+ externalIds,
+ externalIdCache,
+ i,
+ i,
+ null,
+ gitRefUpdated);
}
}
@@ -194,6 +220,7 @@
private final ExternalIdCache externalIdCache;
private final Provider<PersonIdent> serverIdent;
private final Provider<IdentifiedUser> identifiedUser;
+ private final GitReferenceUpdated gitRefUpdated;
@Inject
public User(
@@ -204,7 +231,8 @@
ExternalIds externalIds,
ExternalIdCache externalIdCache,
@GerritPersonIdent Provider<PersonIdent> serverIdent,
- Provider<IdentifiedUser> identifiedUser) {
+ Provider<IdentifiedUser> identifiedUser,
+ GitReferenceUpdated gitRefUpdated) {
this.repoManager = repoManager;
this.accountCache = accountCache;
this.allUsersName = allUsersName;
@@ -213,9 +241,11 @@
this.externalIdCache = externalIdCache;
this.serverIdent = serverIdent;
this.identifiedUser = identifiedUser;
+ this.gitRefUpdated = gitRefUpdated;
}
public ExternalIdsUpdate create() {
+ IdentifiedUser user = identifiedUser.get();
PersonIdent i = serverIdent.get();
return new ExternalIdsUpdate(
repoManager,
@@ -224,8 +254,10 @@
metricMaker,
externalIds,
externalIdCache,
- createPersonIdent(i, identifiedUser.get()),
- i);
+ createPersonIdent(i, user),
+ i,
+ user,
+ gitRefUpdated);
}
private PersonIdent createPersonIdent(PersonIdent ident, IdentifiedUser user) {
@@ -253,6 +285,8 @@
private final ExternalIdCache externalIdCache;
private final PersonIdent committerIdent;
private final PersonIdent authorIdent;
+ @Nullable private final IdentifiedUser currentUser;
+ private final GitReferenceUpdated gitRefUpdated;
private final Runnable afterReadRevision;
private final Retryer<RefsMetaExternalIdsUpdate> retryer;
private final Counter0 updateCount;
@@ -265,7 +299,9 @@
ExternalIds externalIds,
ExternalIdCache externalIdCache,
PersonIdent committerIdent,
- PersonIdent authorIdent) {
+ PersonIdent authorIdent,
+ @Nullable IdentifiedUser currentUser,
+ GitReferenceUpdated gitRefUpdated) {
this(
repoManager,
accountCache,
@@ -275,6 +311,8 @@
externalIdCache,
committerIdent,
authorIdent,
+ currentUser,
+ gitRefUpdated,
Runnables.doNothing(),
RETRYER);
}
@@ -289,6 +327,8 @@
ExternalIdCache externalIdCache,
PersonIdent committerIdent,
PersonIdent authorIdent,
+ @Nullable IdentifiedUser currentUser,
+ GitReferenceUpdated gitRefUpdated,
Runnable afterReadRevision,
Retryer<RefsMetaExternalIdsUpdate> retryer) {
this.repoManager = checkNotNull(repoManager, "repoManager");
@@ -298,6 +338,8 @@
this.externalIds = checkNotNull(externalIds, "externalIds");
this.externalIdCache = checkNotNull(externalIdCache, "externalIdCache");
this.authorIdent = checkNotNull(authorIdent, "authorIdent");
+ this.currentUser = currentUser;
+ this.gitRefUpdated = checkNotNull(gitRefUpdated, "gitRefUpdated");
this.afterReadRevision = checkNotNull(afterReadRevision, "afterReadRevision");
this.retryer = checkNotNull(retryer, "retryer");
this.updateCount =
@@ -732,13 +774,26 @@
NoteMap noteMap,
UpdatedExternalIds updatedExtIds)
throws IOException {
- ObjectId newRev = commit(repo, rw, ins, rev, noteMap, COMMIT_MSG, committerIdent, authorIdent);
+ ObjectId newRev =
+ commit(
+ allUsersName,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ COMMIT_MSG,
+ committerIdent,
+ authorIdent,
+ currentUser,
+ gitRefUpdated);
updateCount.increment();
return RefsMetaExternalIdsUpdate.create(rev, newRev, updatedExtIds);
}
/** Commits updates to the external IDs. */
public static ObjectId commit(
+ Project.NameKey project,
Repository repo,
RevWalk rw,
ObjectInserter ins,
@@ -746,7 +801,9 @@
NoteMap noteMap,
String commitMessage,
PersonIdent committerIdent,
- PersonIdent authorIdent)
+ PersonIdent authorIdent,
+ @Nullable IdentifiedUser user,
+ GitReferenceUpdated gitRefUpdated)
throws IOException {
CommitBuilder cb = new CommitBuilder();
cb.setMessage(commitMessage);
@@ -793,6 +850,7 @@
default:
throw new IOException("Updating external IDs failed with " + res);
}
+ gitRefUpdated.fire(project, u, user != null ? user.getAccount() : null);
return rw.parseCommit(commitId);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index f8539d9..0720a4e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -34,7 +34,9 @@
import com.google.gerrit.extensions.common.EmailInfo;
import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.SshKeyInfo;
+import com.google.gerrit.extensions.common.SshKeyInput;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -219,9 +221,9 @@
public void setActive(boolean active) throws RestApiException {
try {
if (active) {
- putActive.apply(account, new PutActive.Input());
+ putActive.apply(account, new Input());
} else {
- deleteActive.apply(account, new DeleteActive.Input());
+ deleteActive.apply(account, new Input());
}
} catch (Exception e) {
throw asRestApiException("Cannot set active", e);
@@ -423,7 +425,7 @@
@Override
public SshKeyInfo addSshKey(String key) throws RestApiException {
- AddSshKey.Input in = new AddSshKey.Input();
+ SshKeyInput in = new SshKeyInput();
in.raw = RawInputUtil.create(key);
try {
return addSshKey.apply(account, in).value();
@@ -490,7 +492,7 @@
@Override
public void index() throws RestApiException {
try {
- index.apply(account, new Index.Input());
+ index.apply(account, new Input());
} catch (Exception e) {
throw asRestApiException("Cannot index account", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index 0fba74a..6c7fa6f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -41,10 +41,12 @@
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.CommitMessageInput;
import com.google.gerrit.extensions.common.EditInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.PureRevertInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
+import com.google.gerrit.extensions.common.TopicInput;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -435,7 +437,7 @@
@Override
public void topic(String topic) throws RestApiException {
- PutTopic.Input in = new PutTopic.Input();
+ TopicInput in = new TopicInput();
in.topic = topic;
try {
putTopic.apply(change, in);
@@ -646,7 +648,7 @@
@Override
public void index() throws RestApiException {
try {
- index.apply(change, new Index.Input());
+ index.apply(change, new Input());
} catch (Exception e) {
throw asRestApiException("Cannot index change", e);
}
@@ -658,9 +660,9 @@
// StarredChangesUtil.
try {
if (ignore) {
- this.ignore.apply(change, new Ignore.Input());
+ this.ignore.apply(change, new Input());
} else {
- unignore.apply(change, new Unignore.Input());
+ unignore.apply(change, new Input());
}
} catch (OrmException | IllegalLabelException e) {
throw asRestApiException("Cannot ignore change", e);
@@ -682,9 +684,9 @@
// StarredChangesUtil.
try {
if (reviewed) {
- markAsReviewed.apply(change, new MarkAsReviewed.Input());
+ markAsReviewed.apply(change, new Input());
} else {
- markAsUnreviewed.apply(change, new MarkAsUnreviewed.Input());
+ markAsUnreviewed.apply(change, new Input());
}
} catch (OrmException | IllegalLabelException e) {
throw asRestApiException(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
index d1b57e6..823e771 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
@@ -19,6 +19,7 @@
import com.google.gerrit.extensions.api.changes.ChangeEditApi;
import com.google.gerrit.extensions.api.changes.PublishChangeEditInput;
import com.google.gerrit.extensions.common.EditInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.IdString;
@@ -106,7 +107,7 @@
@Override
public void delete() throws RestApiException {
try {
- deleteChangeEdit.apply(changeResource, new DeleteChangeEdit.Input());
+ deleteChangeEdit.apply(changeResource, new Input());
} catch (Exception e) {
throw asRestApiException("Cannot delete change edit", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 65bbc47..7ecfce7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -36,8 +36,10 @@
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.common.DescriptionInput;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.common.FileInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.MergeableInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.common.TestSubmitRuleInput;
@@ -300,13 +302,13 @@
@Override
public void setReviewed(String path, boolean reviewed) throws RestApiException {
try {
- RestModifyView<FileResource, Reviewed.Input> view;
+ RestModifyView<FileResource, Input> view;
if (reviewed) {
view = putReviewed;
} else {
view = deleteReviewed;
}
- view.apply(files.parse(revision, IdString.fromDecoded(path)), new Reviewed.Input());
+ view.apply(files.parse(revision, IdString.fromDecoded(path)), new Input());
} catch (Exception e) {
throw asRestApiException("Cannot update reviewed flag", e);
}
@@ -565,7 +567,7 @@
@Override
public void description(String description) throws RestApiException {
- PutDescription.Input in = new PutDescription.Input();
+ DescriptionInput in = new DescriptionInput();
in.description = description;
try {
putDescription.apply(revision, in);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
index 42213f7..39ee086 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -18,9 +18,13 @@
import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.DescriptionInput;
import com.google.gerrit.extensions.common.GroupAuditEventInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.GroupOptionsInfo;
+import com.google.gerrit.extensions.common.Input;
+import com.google.gerrit.extensions.common.NameInput;
+import com.google.gerrit.extensions.common.OwnerInput;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.group.AddMembers;
import com.google.gerrit.server.group.AddSubgroups;
@@ -138,7 +142,7 @@
@Override
public void name(String name) throws RestApiException {
- PutName.Input in = new PutName.Input();
+ NameInput in = new NameInput();
in.name = name;
try {
putName.apply(rsrc, in);
@@ -158,7 +162,7 @@
@Override
public void owner(String owner) throws RestApiException {
- PutOwner.Input in = new PutOwner.Input();
+ OwnerInput in = new OwnerInput();
in.owner = owner;
try {
putOwner.apply(rsrc, in);
@@ -174,7 +178,7 @@
@Override
public void description(String description) throws RestApiException {
- PutDescription.Input in = new PutDescription.Input();
+ DescriptionInput in = new DescriptionInput();
in.description = description;
try {
putDescription.apply(rsrc, in);
@@ -269,7 +273,7 @@
@Override
public void index() throws RestApiException {
try {
- index.apply(rsrc, new Index.Input());
+ index.apply(rsrc, new Input());
} catch (Exception e) {
throw asRestApiException("Cannot index group", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
index 2fc2e50..71f7832 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.api.plugins;
import com.google.gerrit.extensions.api.plugins.PluginApi;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.plugins.DisablePlugin;
@@ -57,16 +58,16 @@
@Override
public void enable() throws RestApiException {
- enable.apply(resource, new EnablePlugin.Input());
+ enable.apply(resource, new Input());
}
@Override
public void disable() throws RestApiException {
- disable.apply(resource, new DisablePlugin.Input());
+ disable.apply(resource, new Input());
}
@Override
public void reload() throws RestApiException {
- reload.apply(resource, new ReloadPlugin.Input());
+ reload.apply(resource, new Input());
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
index 642791a..aee9b3f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
@@ -20,6 +20,7 @@
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.ReflogEntryInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -98,7 +99,7 @@
@Override
public void delete() throws RestApiException {
try {
- deleteBranch.apply(resource(), new DeleteBranch.Input());
+ deleteBranch.apply(resource(), new Input());
} catch (Exception e) {
throw asRestApiException("Cannot delete branch", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
index 702a7e9..9490075 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
@@ -22,14 +22,18 @@
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ListProjects;
import com.google.gerrit.server.project.ListProjects.FilterType;
import com.google.gerrit.server.project.ProjectsCollection;
+import com.google.gerrit.server.project.QueryProjects;
+import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.util.List;
import java.util.SortedMap;
@Singleton
@@ -37,15 +41,18 @@
private final ProjectsCollection projects;
private final ProjectApiImpl.Factory api;
private final Provider<ListProjects> listProvider;
+ private final Provider<QueryProjects> queryProvider;
@Inject
ProjectsImpl(
ProjectsCollection projects,
ProjectApiImpl.Factory api,
- Provider<ListProjects> listProvider) {
+ Provider<ListProjects> listProvider,
+ Provider<QueryProjects> queryProvider) {
this.projects = projects;
this.api = api;
this.listProvider = listProvider;
+ this.queryProvider = queryProvider;
}
@Override
@@ -124,4 +131,32 @@
return lp.apply();
}
+
+ @Override
+ public QueryRequest query() {
+ return new QueryRequest() {
+ @Override
+ public List<ProjectInfo> get() throws RestApiException {
+ return ProjectsImpl.this.query(this);
+ }
+ };
+ }
+
+ @Override
+ public QueryRequest query(String query) {
+ return query().withQuery(query);
+ }
+
+ private List<ProjectInfo> query(QueryRequest r) throws RestApiException {
+ try {
+ QueryProjects myQueryProjects = queryProvider.get();
+ myQueryProjects.setQuery(r.getQuery());
+ myQueryProjects.setLimit(r.getLimit());
+ myQueryProjects.setStart(r.getStart());
+
+ return myQueryProjects.apply(TopLevelResource.INSTANCE);
+ } catch (OrmException e) {
+ throw new RestApiException("Cannot query projects", e);
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/TagApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/TagApiImpl.java
index 283d117..9f19c6d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/TagApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/TagApiImpl.java
@@ -19,6 +19,7 @@
import com.google.gerrit.extensions.api.projects.TagApi;
import com.google.gerrit.extensions.api.projects.TagInfo;
import com.google.gerrit.extensions.api.projects.TagInput;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.project.CreateTag;
@@ -81,7 +82,7 @@
@Override
public void delete() throws RestApiException {
try {
- deleteTag.apply(resource(), new DeleteTag.Input());
+ deleteTag.apply(resource(), new Input());
} catch (Exception e) {
throw asRestApiException("Cannot delete tag", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
index 18d3482..1e3e502 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
@@ -17,6 +17,7 @@
import com.google.common.base.Strings;
import com.google.gerrit.extensions.common.DiffWebLinkInfo;
import com.google.gerrit.extensions.common.EditInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsCreate;
import com.google.gerrit.extensions.restapi.AcceptsDelete;
@@ -159,8 +160,7 @@
}
}
- public static class DeleteFile implements RestModifyView<ChangeResource, DeleteFile.Input> {
- public static class Input {}
+ public static class DeleteFile implements RestModifyView<ChangeResource, Input> {
interface Factory {
DeleteFile create(String path);
@@ -176,7 +176,7 @@
}
@Override
- public Response<?> apply(ChangeResource rsrc, DeleteFile.Input in)
+ public Response<?> apply(ChangeResource rsrc, Input in)
throws IOException, AuthException, ResourceConflictException, OrmException,
PermissionBackendException {
return deleteContent.apply(rsrc, path);
@@ -342,9 +342,7 @@
* restoring a file to its previous contents.
*/
@Singleton
- public static class DeleteContent
- implements RestModifyView<ChangeEditResource, DeleteContent.Input> {
- public static class Input {}
+ public static class DeleteContent implements RestModifyView<ChangeEditResource, Input> {
private final ChangeEditModifier editModifier;
private final GitRepositoryManager repositoryManager;
@@ -356,7 +354,7 @@
}
@Override
- public Response<?> apply(ChangeEditResource rsrc, DeleteContent.Input input)
+ public Response<?> apply(ChangeEditResource rsrc, Input input)
throws AuthException, ResourceConflictException, OrmException, IOException,
PermissionBackendException {
return apply(rsrc.getChangeResource(), rsrc.getPath());
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 805512e..a56612c5 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
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsPost;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -126,6 +127,11 @@
}
private boolean canRead(ChangeNotes notes) throws PermissionBackendException {
- return permissionBackend.user(user).change(notes).database(db).test(ChangePermission.READ);
+ try {
+ permissionBackend.user(user).change(notes).database(db).check(ChangePermission.READ);
+ return true;
+ } catch (AuthException e) {
+ return false;
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteAssignee.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteAssignee.java
index d3feb31..8d8d72e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteAssignee.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteAssignee.java
@@ -16,6 +16,7 @@
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Account;
@@ -25,7 +26,6 @@
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountLoader;
-import com.google.gerrit.server.change.DeleteAssignee.Input;
import com.google.gerrit.server.extensions.events.AssigneeChanged;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -45,7 +45,6 @@
@Singleton
public class DeleteAssignee
extends RetryingRestModifyView<ChangeResource, Input, Response<AccountInfo>> {
- public static class Input {}
private final ChangeMessagesUtil cmUtil;
private final Provider<ReviewDb> db;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChange.java
index af26e8a..69f6178 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChange.java
@@ -17,13 +17,13 @@
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.DeleteChange.Input;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -39,7 +39,6 @@
@Singleton
public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
implements UiAction<ChangeResource> {
- public static class Input {}
private final Provider<ReviewDb> db;
private final Provider<DeleteChangeOp> opProvider;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeEdit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeEdit.java
index e2e3920..480aca1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeEdit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeEdit.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.change.DeleteChangeEdit.Input;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
import com.google.gwtorm.server.OrmException;
@@ -29,7 +29,6 @@
@Singleton
public class DeleteChangeEdit implements RestModifyView<ChangeResource, Input> {
- public static class Input {}
private final ChangeEditUtil editUtil;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java
index 68db189..6d82139 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java
@@ -18,6 +18,7 @@
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -26,7 +27,6 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.change.DeleteDraftComment.Input;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
@@ -44,7 +44,6 @@
@Singleton
public class DeleteDraftComment
extends RetryingRestModifyView<DraftCommentResource, Input, Response<CommentInfo>> {
- static class Input {}
private final Provider<ReviewDb> db;
private final CommentsUtil commentsUtil;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Ignore.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Ignore.java
index 46dabdf..c2c2d1f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Ignore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Ignore.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
@@ -30,12 +31,9 @@
import org.slf4j.LoggerFactory;
@Singleton
-public class Ignore
- implements RestModifyView<ChangeResource, Ignore.Input>, UiAction<ChangeResource> {
+public class Ignore implements RestModifyView<ChangeResource, Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(Ignore.class);
- public static class Input {}
-
private final StarredChangesUtil stars;
@Inject
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
index 7c4d158..85e13cc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.change.Index.Input;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -34,7 +34,6 @@
@Singleton
public class Index extends RetryingRestModifyView<ChangeResource, Input, Response<?>> {
- public static class Input {}
private final Provider<ReviewDb> db;
private final PermissionBackend permissionBackend;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsReviewed.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsReviewed.java
index 265b2b0..9e77805 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsReviewed.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsReviewed.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -31,11 +32,9 @@
@Singleton
public class MarkAsReviewed
- implements RestModifyView<ChangeResource, MarkAsReviewed.Input>, UiAction<ChangeResource> {
+ implements RestModifyView<ChangeResource, Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(MarkAsReviewed.class);
- public static class Input {}
-
private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
private final StarredChangesUtil stars;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsUnreviewed.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsUnreviewed.java
index 6de84ee..436548b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsUnreviewed.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MarkAsUnreviewed.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiAction;
@@ -30,11 +31,9 @@
@Singleton
public class MarkAsUnreviewed
- implements RestModifyView<ChangeResource, MarkAsUnreviewed.Input>, UiAction<ChangeResource> {
+ implements RestModifyView<ChangeResource, Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(MarkAsUnreviewed.class);
- public static class Input {}
-
private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
private final StarredChangesUtil stars;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDescription.java
index 4c9cf23..0d932ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDescription.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDescription.java
@@ -16,7 +16,7 @@
import com.google.common.base.Strings;
import com.google.gerrit.common.TimeUtil;
-import com.google.gerrit.extensions.restapi.DefaultInput;
+import com.google.gerrit.extensions.common.DescriptionInput;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
@@ -42,16 +42,12 @@
@Singleton
public class PutDescription
- extends RetryingRestModifyView<RevisionResource, PutDescription.Input, Response<String>>
+ extends RetryingRestModifyView<RevisionResource, DescriptionInput, Response<String>>
implements UiAction<RevisionResource> {
private final Provider<ReviewDb> dbProvider;
private final ChangeMessagesUtil cmUtil;
private final PatchSetUtil psUtil;
- public static class Input {
- @DefaultInput public String description;
- }
-
@Inject
PutDescription(
Provider<ReviewDb> dbProvider,
@@ -66,11 +62,11 @@
@Override
protected Response<String> applyImpl(
- BatchUpdate.Factory updateFactory, RevisionResource rsrc, Input input)
+ BatchUpdate.Factory updateFactory, RevisionResource rsrc, DescriptionInput input)
throws UpdateException, RestApiException, PermissionBackendException {
rsrc.permissions().check(ChangePermission.EDIT_DESCRIPTION);
- Op op = new Op(input != null ? input : new Input(), rsrc.getPatchSet().getId());
+ Op op = new Op(input != null ? input : new DescriptionInput(), rsrc.getPatchSet().getId());
try (BatchUpdate u =
updateFactory.create(
dbProvider.get(), rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
@@ -83,13 +79,13 @@
}
private class Op implements BatchUpdateOp {
- private final Input input;
+ private final DescriptionInput input;
private final PatchSet.Id psId;
private String oldDescription;
private String newDescription;
- Op(Input input, PatchSet.Id psId) {
+ Op(DescriptionInput input, PatchSet.Id psId) {
this.input = input;
this.psId = psId;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
index 8b5608b..33b3d7e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
@@ -16,8 +16,8 @@
import com.google.common.base.Strings;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.common.TopicInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
@@ -26,7 +26,6 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.change.PutTopic.Input;
import com.google.gerrit.server.extensions.events.TopicEdited;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -44,16 +43,12 @@
import com.google.inject.Singleton;
@Singleton
-public class PutTopic extends RetryingRestModifyView<ChangeResource, Input, Response<String>>
+public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput, Response<String>>
implements UiAction<ChangeResource> {
private final Provider<ReviewDb> dbProvider;
private final ChangeMessagesUtil cmUtil;
private final TopicEdited topicEdited;
- public static class Input {
- @DefaultInput public String topic;
- }
-
@Inject
PutTopic(
Provider<ReviewDb> dbProvider,
@@ -68,7 +63,7 @@
@Override
protected Response<String> applyImpl(
- BatchUpdate.Factory updateFactory, ChangeResource req, Input input)
+ BatchUpdate.Factory updateFactory, ChangeResource req, TopicInput input)
throws UpdateException, RestApiException, PermissionBackendException {
req.permissions().check(ChangePermission.EDIT_TOPIC_NAME);
@@ -79,7 +74,7 @@
String.format("topic length exceeds the limit (%s)", ChangeUtil.TOPIC_MAX_LENGTH));
}
- Op op = new Op(input != null ? input : new Input());
+ Op op = new Op(input != null ? input : new TopicInput());
try (BatchUpdate u =
updateFactory.create(
dbProvider.get(), req.getChange().getProject(), req.getUser(), TimeUtil.nowTs())) {
@@ -90,13 +85,13 @@
}
private class Op implements BatchUpdateOp {
- private final Input input;
+ private final TopicInput input;
private Change change;
private String oldTopicName;
private String newTopicName;
- Op(Input input) {
+ Op(TopicInput input) {
this.input = input;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeEdit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeEdit.java
index 38a695a..2909827 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeEdit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeEdit.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsPost;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -68,8 +69,7 @@
}
@Singleton
- public static class Rebase implements RestModifyView<ChangeResource, Rebase.Input> {
- public static class Input {}
+ public static class Rebase implements RestModifyView<ChangeResource, Input> {
private final GitRepositoryManager repositoryManager;
private final ChangeEditModifier editModifier;
@@ -81,7 +81,7 @@
}
@Override
- public Response<?> apply(ChangeResource rsrc, Rebase.Input in)
+ public Response<?> apply(ChangeResource rsrc, Input in)
throws AuthException, ResourceConflictException, IOException, OrmException,
PermissionBackendException {
Project.NameKey project = rsrc.getProject();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebuild.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebuild.java
index 682b45f..a3ed670 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebuild.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebuild.java
@@ -16,6 +16,7 @@
import static java.util.stream.Collectors.joining;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -23,7 +24,6 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.change.Rebuild.Input;
import com.google.gerrit.server.notedb.ChangeBundle;
import com.google.gerrit.server.notedb.ChangeBundleReader;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -40,7 +40,6 @@
@Singleton
public class Rebuild implements RestModifyView<ChangeResource, Input> {
- public static class Input {}
private final Provider<ReviewDb> db;
private final NotesMigration migration;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
index 0d25d35..c412adf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Reviewed.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -22,7 +23,6 @@
import com.google.inject.Singleton;
public class Reviewed {
- public static class Input {}
@Singleton
public static class PutReviewed implements RestModifyView<FileResource, Input> {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Unignore.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Unignore.java
index 2bad16c..39a82d7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Unignore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Unignore.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiAction;
@@ -26,12 +27,9 @@
import org.slf4j.LoggerFactory;
@Singleton
-public class Unignore
- implements RestModifyView<ChangeResource, Unignore.Input>, UiAction<ChangeResource> {
+public class Unignore implements RestModifyView<ChangeResource, Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(Unignore.class);
- public static class Input {}
-
private final StarredChangesUtil stars;
@Inject
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/DeleteTask.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/DeleteTask.java
index 29ca20f..d20589a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/DeleteTask.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/DeleteTask.java
@@ -19,16 +19,15 @@
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.config.DeleteTask.Input;
import com.google.gerrit.server.git.WorkQueue.Task;
import com.google.inject.Singleton;
@Singleton
@RequiresAnyCapability({KILL_TASK, MAINTAIN_SERVER})
public class DeleteTask implements RestModifyView<TaskResource, Input> {
- public static class Input {}
@Override
public Response<?> apply(TaskResource rsrc, Input input) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/FlushCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/FlushCache.java
index 366dae1..5d2ec36 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/FlushCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/FlushCache.java
@@ -18,11 +18,11 @@
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.config.FlushCache.Input;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +33,6 @@
@RequiresAnyCapability({FLUSH_CACHES, MAINTAIN_SERVER})
@Singleton
public class FlushCache implements RestModifyView<CacheResource, Input> {
- public static class Input {}
public static final String WEB_SESSIONS = "web_sessions";
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
index 50f4975..23f2526 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -34,8 +34,6 @@
import java.util.EnumSet;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
@@ -111,8 +109,6 @@
}
private final Path basePath;
- private final Lock namesUpdateLock;
- private volatile SortedSet<Project.NameKey> names = new TreeSet<>();
@Inject
LocalDiskRepositoryManager(SitePaths site, @GerritServerConfig Config cfg) {
@@ -120,8 +116,6 @@
if (basePath == null) {
throw new IllegalStateException("gerrit.basePath must be configured");
}
-
- namesUpdateLock = new ReentrantLock(true /* fair */);
}
/**
@@ -144,32 +138,7 @@
if (isUnreasonableName(name)) {
throw new RepositoryNotFoundException("Invalid name: " + name);
}
- File gitDir = path.resolve(name.get()).toFile();
- if (!names.contains(name)) {
- // The this.names list does not hold the project-name but it can still exist
- // on disk; for instance when the project has been created directly on the
- // file-system through replication.
- //
- if (!name.get().endsWith(Constants.DOT_GIT_EXT)) {
- if (FileKey.resolve(gitDir, FS.DETECTED) != null) {
- onCreateProject(name);
- } else {
- throw new RepositoryNotFoundException(gitDir);
- }
- } else {
- final File directory = gitDir;
- if (FileKey.isGitRepository(new File(directory, Constants.DOT_GIT), FS.DETECTED)) {
- onCreateProject(name);
- } else if (FileKey.isGitRepository(
- new File(directory.getParentFile(), directory.getName() + Constants.DOT_GIT_EXT),
- FS.DETECTED)) {
- onCreateProject(name);
- } else {
- throw new RepositoryNotFoundException(gitDir);
- }
- }
- }
- final FileKey loc = FileKey.lenient(gitDir, FS.DETECTED);
+ FileKey loc = FileKey.lenient(path.resolve(name.get()).toFile(), FS.DETECTED);
try {
return RepositoryCache.open(loc);
} catch (IOException e1) {
@@ -189,26 +158,24 @@
}
File dir = FileKey.resolve(path.resolve(name.get()).toFile(), FS.DETECTED);
- FileKey loc;
if (dir != null) {
// Already exists on disk, use the repository we found.
//
Project.NameKey onDiskName = getProjectName(path, dir.getCanonicalFile().toPath());
- onCreateProject(onDiskName);
- loc = FileKey.exact(dir, FS.DETECTED);
-
- if (!names.contains(name)) {
+ if (!onDiskName.equals(name)) {
throw new RepositoryCaseMismatchException(name);
}
- } else {
- // It doesn't exist under any of the standard permutations
- // of the repository name, so prefer the standard bare name.
- //
- String n = name.get() + Constants.DOT_GIT_EXT;
- loc = FileKey.exact(path.resolve(n).toFile(), FS.DETECTED);
+
+ throw new IllegalStateException("Repository already exists: " + name);
}
+ // It doesn't exist under any of the standard permutations
+ // of the repository name, so prefer the standard bare name.
+ //
+ String n = name.get() + Constants.DOT_GIT_EXT;
+ FileKey loc = FileKey.exact(path.resolve(n).toFile(), FS.DETECTED);
+
try {
Repository db = RepositoryCache.open(loc, false);
db.create(true /* bare */);
@@ -231,8 +198,6 @@
"Failed to create ref log for %s in repository %s", RefNames.REFS_CONFIG, name));
}
- onCreateProject(name);
-
return db;
} catch (IOException e1) {
final RepositoryNotFoundException e2;
@@ -242,17 +207,6 @@
}
}
- private void onCreateProject(Project.NameKey newProjectName) {
- namesUpdateLock.lock();
- try {
- SortedSet<Project.NameKey> n = new TreeSet<>(names);
- n.add(newProjectName);
- names = Collections.unmodifiableSortedSet(n);
- } finally {
- namesUpdateLock.unlock();
- }
- }
-
private boolean isUnreasonableName(Project.NameKey nameKey) {
final String name = nameKey.get();
@@ -281,21 +235,9 @@
@Override
public SortedSet<Project.NameKey> list() {
- // The results of this method are cached by ProjectCacheImpl. Control only
- // enters here if the cache was flushed by the administrator to force
- // scanning the filesystem.
- // Don't rely on the cached names collection but update it to contain
- // the set of found project names
ProjectVisitor visitor = new ProjectVisitor(basePath);
scanProjects(visitor);
-
- namesUpdateLock.lock();
- try {
- names = Collections.unmodifiableSortedSet(visitor.found);
- } finally {
- namesUpdateLock.unlock();
- }
- return names;
+ return Collections.unmodifiableSortedSet(visitor.found);
}
protected void scanProjects(ProjectVisitor visitor) {
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 b7aa416..aaed2e7 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
@@ -669,6 +669,11 @@
}
addMessage("");
}
+
+ // TODO(xchangcheng): remove after migrating tools which are using this magic branch.
+ if (magicBranch != null && magicBranch.publish) {
+ addMessage("Pushing to refs/publish/* is deprecated, use refs/for/* instead.");
+ }
}
private void insertChangesAndPatchSets() {
@@ -1165,6 +1170,8 @@
)
boolean draft;
+ boolean publish;
+
@Option(name = "--private", usage = "mark new/updated change as private")
boolean isPrivate;
@@ -1289,6 +1296,7 @@
NotesMigration notesMigration) {
this.cmd = cmd;
this.draft = cmd.getRefName().startsWith(MagicBranch.NEW_DRAFT_CHANGE);
+ this.publish = cmd.getRefName().startsWith(MagicBranch.NEW_PUBLISH_CHANGE);
this.labelTypes = labelTypes;
this.notesMigration = notesMigration;
GeneralPreferencesInfo prefs = user.getAccount().getGeneralPreferencesInfo();
@@ -2563,7 +2571,11 @@
}
if (isConfig(cmd)) {
logDebug("Reloading project in cache");
- projectCache.evict(project);
+ try {
+ projectCache.evict(project);
+ } catch (IOException e) {
+ log.warn("Cannot evict from project cache, name key: " + project.getName(), e);
+ }
ProjectState ps = projectCache.get(project.getNameKey());
try {
logDebug("Updating project description");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
index b6bcb3b..24b3f36 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
@@ -204,8 +204,7 @@
return result;
}
- static class PutMember implements RestModifyView<GroupResource, PutMember.Input> {
- static class Input {}
+ static class PutMember implements RestModifyView<GroupResource, Input> {
private final AddMembers put;
private final String id;
@@ -216,7 +215,7 @@
}
@Override
- public AccountInfo apply(GroupResource resource, PutMember.Input input)
+ public AccountInfo apply(GroupResource resource, Input input)
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
IOException, ConfigInvalidException {
AddMembers.Input in = new AddMembers.Input();
@@ -234,7 +233,7 @@
}
@Singleton
- static class UpdateMember implements RestModifyView<MemberResource, PutMember.Input> {
+ static class UpdateMember implements RestModifyView<MemberResource, Input> {
private final GetMember get;
@Inject
@@ -243,7 +242,7 @@
}
@Override
- public AccountInfo apply(MemberResource resource, PutMember.Input input) throws OrmException {
+ public AccountInfo apply(MemberResource resource, Input input) throws OrmException {
// Do nothing, the user is already a member.
return get.apply(resource);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddSubgroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddSubgroups.java
index 2ce168f..f60a8ce 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddSubgroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddSubgroups.java
@@ -114,8 +114,7 @@
return result;
}
- static class PutSubgroup implements RestModifyView<GroupResource, PutSubgroup.Input> {
- static class Input {}
+ static class PutSubgroup implements RestModifyView<GroupResource, Input> {
private final AddSubgroups addSubgroups;
private final String id;
@@ -144,7 +143,7 @@
}
@Singleton
- static class UpdateSubgroup implements RestModifyView<SubgroupResource, PutSubgroup.Input> {
+ static class UpdateSubgroup implements RestModifyView<SubgroupResource, Input> {
private final Provider<GetSubgroup> get;
@Inject
@@ -153,7 +152,7 @@
}
@Override
- public GroupInfo apply(SubgroupResource resource, PutSubgroup.Input input) throws OrmException {
+ public GroupInfo apply(SubgroupResource resource, Input input) throws OrmException {
// Do nothing, the group is already included.
return get.get().apply(resource);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
index 1069e1c..64de014 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
@@ -82,8 +82,7 @@
}
@Singleton
- static class DeleteMember implements RestModifyView<MemberResource, DeleteMember.Input> {
- static class Input {}
+ static class DeleteMember implements RestModifyView<MemberResource, Input> {
private final Provider<DeleteMembers> delete;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteSubgroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteSubgroups.java
index 14df51b..43c7d59 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteSubgroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteSubgroups.java
@@ -82,8 +82,7 @@
}
@Singleton
- static class DeleteSubgroup implements RestModifyView<SubgroupResource, DeleteSubgroup.Input> {
- static class Input {}
+ static class DeleteSubgroup implements RestModifyView<SubgroupResource, Input> {
private final Provider<DeleteSubgroups> delete;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java
index a2660f2..233f36b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Groups.java
@@ -47,6 +47,29 @@
public class Groups {
/**
+ * Returns the {@code AccountGroup} for the specified ID if it exists.
+ *
+ * @param db the {@code ReviewDb} instance to use for lookups
+ * @param groupId the ID of the group
+ * @return the found {@code AccountGroup} if it exists, or else an empty {@code Optional}
+ * @throws OrmException if the group couldn't be retrieved from ReviewDb
+ */
+ public Optional<InternalGroup> getGroup(ReviewDb db, AccountGroup.Id groupId)
+ throws OrmException, NoSuchGroupException {
+ Optional<AccountGroup> accountGroup = Optional.ofNullable(db.accountGroups().get(groupId));
+
+ if (!accountGroup.isPresent()) {
+ return Optional.empty();
+ }
+
+ AccountGroup.UUID groupUuid = accountGroup.get().getGroupUUID();
+ ImmutableSet<Account.Id> members = getMembers(db, groupUuid).collect(toImmutableSet());
+ ImmutableSet<AccountGroup.UUID> subgroups =
+ getSubgroups(db, groupUuid).collect(toImmutableSet());
+ return accountGroup.map(group -> InternalGroup.create(group, members, subgroups));
+ }
+
+ /**
* Returns the {@code InternalGroup} for the specified UUID if it exists.
*
* @param db the {@code ReviewDb} instance to use for lookups
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java
index b61f954..b2845fa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Index.java
@@ -14,13 +14,13 @@
package com.google.gerrit.server.group;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.group.Index.Input;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -28,7 +28,6 @@
@Singleton
public class Index implements RestModifyView<GroupResource, Input> {
- public static class Input {}
private final GroupCache groupCache;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java
index 3d6feea..757ad31 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutDescription.java
@@ -17,15 +17,14 @@
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.extensions.common.DescriptionInput;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.group.PutDescription.Input;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -34,11 +33,7 @@
import java.util.Objects;
@Singleton
-public class PutDescription implements RestModifyView<GroupResource, Input> {
- public static class Input {
- @DefaultInput public String description;
- }
-
+public class PutDescription implements RestModifyView<GroupResource, DescriptionInput> {
private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@@ -50,11 +45,11 @@
}
@Override
- public Response<String> apply(GroupResource resource, Input input)
+ public Response<String> apply(GroupResource resource, DescriptionInput input)
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
IOException {
if (input == null) {
- input = new Input(); // Delete would set description to null.
+ input = new DescriptionInput(); // Delete would set description to null.
}
GroupDescription.Internal internalGroup =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java
index 75a7eb5..3e7fd41 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java
@@ -18,16 +18,15 @@
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.extensions.common.NameInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.group.PutName.Input;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -35,11 +34,7 @@
import java.io.IOException;
@Singleton
-public class PutName implements RestModifyView<GroupResource, Input> {
- public static class Input {
- @DefaultInput public String name;
- }
-
+public class PutName implements RestModifyView<GroupResource, NameInput> {
private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@@ -50,7 +45,7 @@
}
@Override
- public String apply(GroupResource rsrc, Input input)
+ public String apply(GroupResource rsrc, NameInput input)
throws MethodNotAllowedException, AuthException, BadRequestException,
ResourceConflictException, ResourceNotFoundException, OrmException, IOException {
GroupDescription.Internal internalGroup =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java
index 20e1dbe..7723b27 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java
@@ -18,16 +18,15 @@
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.common.OwnerInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.group.PutOwner.Input;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -35,11 +34,7 @@
import java.io.IOException;
@Singleton
-public class PutOwner implements RestModifyView<GroupResource, Input> {
- public static class Input {
- @DefaultInput public String owner;
- }
-
+public class PutOwner implements RestModifyView<GroupResource, OwnerInput> {
private final GroupsCollection groupsCollection;
private final Provider<GroupsUpdate> groupsUpdateProvider;
private final Provider<ReviewDb> db;
@@ -58,7 +53,7 @@
}
@Override
- public GroupInfo apply(GroupResource resource, Input input)
+ public GroupInfo apply(GroupResource resource, OwnerInput input)
throws ResourceNotFoundException, MethodNotAllowedException, AuthException,
BadRequestException, UnprocessableEntityException, OrmException, IOException {
GroupDescription.Internal internalGroup =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndexModule.java
index 481726b..85d6a7c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndexModule.java
@@ -23,6 +23,8 @@
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.DummyChangeIndex;
import com.google.gerrit.server.index.group.GroupIndex;
+import com.google.gerrit.server.index.project.ProjectIndex;
+import com.google.gerrit.server.project.ProjectData;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.AbstractModule;
@@ -48,6 +50,13 @@
}
}
+ private static class DummyProjectIndexFactory implements ProjectIndex.Factory {
+ @Override
+ public ProjectIndex create(Schema<ProjectData> schema) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
@Override
protected void configure() {
install(new IndexModule(1));
@@ -56,5 +65,6 @@
bind(AccountIndex.Factory.class).toInstance(new DummyAccountIndexFactory());
bind(ChangeIndex.Factory.class).toInstance(new DummyChangeIndexFactory());
bind(GroupIndex.Factory.class).toInstance(new DummyGroupIndexFactory());
+ bind(ProjectIndex.Factory.class).toInstance(new DummyProjectIndexFactory());
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
index 6854a87..8c9a964 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
@@ -45,6 +45,12 @@
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.index.group.GroupIndexerImpl;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
+import com.google.gerrit.server.index.project.ProjectIndexCollection;
+import com.google.gerrit.server.index.project.ProjectIndexDefinition;
+import com.google.gerrit.server.index.project.ProjectIndexRewriter;
+import com.google.gerrit.server.index.project.ProjectIndexer;
+import com.google.gerrit.server.index.project.ProjectIndexerImpl;
+import com.google.gerrit.server.index.project.ProjectSchemaDefinitions;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
@@ -70,7 +76,8 @@
ImmutableList.<SchemaDefinitions<?>>of(
AccountSchemaDefinitions.INSTANCE,
ChangeSchemaDefinitions.INSTANCE,
- GroupSchemaDefinitions.INSTANCE);
+ GroupSchemaDefinitions.INSTANCE,
+ ProjectSchemaDefinitions.INSTANCE);
/** Type of secondary index. */
public static IndexType getIndexType(Injector injector) {
@@ -112,14 +119,22 @@
listener().to(GroupIndexCollection.class);
factory(GroupIndexerImpl.Factory.class);
+ bind(ProjectIndexRewriter.class);
+ bind(ProjectIndexCollection.class);
+ listener().to(ProjectIndexCollection.class);
+ factory(ProjectIndexerImpl.Factory.class);
+
DynamicSet.setOf(binder(), OnlineUpgradeListener.class);
}
@Provides
Collection<IndexDefinition<?, ?, ?>> getIndexDefinitions(
- AccountIndexDefinition accounts, ChangeIndexDefinition changes, GroupIndexDefinition groups) {
+ AccountIndexDefinition accounts,
+ ChangeIndexDefinition changes,
+ GroupIndexDefinition groups,
+ ProjectIndexDefinition projects) {
Collection<IndexDefinition<?, ?, ?>> result =
- ImmutableList.<IndexDefinition<?, ?, ?>>of(accounts, groups, changes);
+ ImmutableList.<IndexDefinition<?, ?, ?>>of(accounts, groups, changes, projects);
Set<String> expected =
FluentIterable.from(ALL_SCHEMA_DEFS).transform(SchemaDefinitions::getName).toSet();
Set<String> actual = FluentIterable.from(result).transform(IndexDefinition::getName).toSet();
@@ -156,6 +171,13 @@
@Provides
@Singleton
+ ProjectIndexer getProjectIndexer(
+ ProjectIndexerImpl.Factory factory, ProjectIndexCollection indexes) {
+ return factory.create(indexes);
+ }
+
+ @Provides
+ @Singleton
@IndexExecutor(INTERACTIVE)
ListeningExecutorService getInteractiveIndexExecutor(
@GerritServerConfig Config config, WorkQueue workQueue) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexUtils.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexUtils.java
index ea9900b..b37ed61 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexUtils.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexUtils.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.index.group.GroupField;
+import com.google.gerrit.server.index.project.ProjectField;
import com.google.gerrit.server.query.change.SingleGroupUser;
import java.io.IOException;
import java.util.Set;
@@ -94,6 +95,13 @@
return user.toString();
}
+ public static Set<String> projectFields(QueryOptions opts) {
+ Set<String> fs = opts.fields();
+ return fs.contains(ProjectField.NAME.getName())
+ ? fs
+ : Sets.union(fs, ImmutableSet.of(ProjectField.NAME.getName()));
+ }
+
private IndexUtils() {
// hide default constructor
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
index 2ab5c55..2e1da65 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
@@ -88,7 +88,7 @@
public void onGitReferenceUpdated(Event event) {
if (allUsersName.get().equals(event.getProjectName())) {
Account.Id accountId = Account.Id.fromRef(event.getRefName());
- if (accountId != null) {
+ if (accountId != null && !event.getRefName().startsWith(RefNames.REFS_STARRED_CHANGES)) {
try {
accountCache.evict(accountId);
} catch (IOException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/AllProjectsIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/AllProjectsIndexer.java
new file mode 100644
index 0000000..a53434e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/AllProjectsIndexer.java
@@ -0,0 +1,112 @@
+// 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.index.project;
+
+import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.index.SiteIndexer;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.index.IndexExecutor;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class AllProjectsIndexer extends SiteIndexer<Project.NameKey, ProjectData, ProjectIndex> {
+
+ private static final Logger log = LoggerFactory.getLogger(AllProjectsIndexer.class);
+
+ private final ListeningExecutorService executor;
+ private final ProjectCache projectCache;
+
+ @Inject
+ AllProjectsIndexer(
+ @IndexExecutor(BATCH) ListeningExecutorService executor, ProjectCache projectCache) {
+ this.executor = executor;
+ this.projectCache = projectCache;
+ }
+
+ @Override
+ public SiteIndexer.Result indexAll(final ProjectIndex index) {
+ ProgressMonitor progress = new TextProgressMonitor(new PrintWriter(progressOut));
+ progress.start(2);
+ List<Project.NameKey> names = collectProjects(progress);
+ return reindexProjects(index, names, progress);
+ }
+
+ private SiteIndexer.Result reindexProjects(
+ ProjectIndex index, List<Project.NameKey> names, ProgressMonitor progress) {
+ progress.beginTask("Reindexing projects", names.size());
+ List<ListenableFuture<?>> futures = new ArrayList<>(names.size());
+ AtomicBoolean ok = new AtomicBoolean(true);
+ AtomicInteger done = new AtomicInteger();
+ AtomicInteger failed = new AtomicInteger();
+ Stopwatch sw = Stopwatch.createStarted();
+ for (Project.NameKey name : names) {
+ String desc = "project " + name;
+ ListenableFuture<?> future =
+ executor.submit(
+ () -> {
+ try {
+ projectCache.evict(name);
+ index.replace(projectCache.get(name).toProjectData());
+ verboseWriter.println("Reindexed " + desc);
+ done.incrementAndGet();
+ } catch (Exception e) {
+ failed.incrementAndGet();
+ throw e;
+ }
+ return null;
+ });
+ addErrorListener(future, desc, progress, ok);
+ futures.add(future);
+ }
+
+ try {
+ Futures.successfulAsList(futures).get();
+ } catch (ExecutionException | InterruptedException e) {
+ log.error("Error waiting on project futures", e);
+ return new SiteIndexer.Result(sw, false, 0, 0);
+ }
+
+ progress.endTask();
+ return new SiteIndexer.Result(sw, ok.get(), done.get(), failed.get());
+ }
+
+ private List<Project.NameKey> collectProjects(ProgressMonitor progress) {
+ progress.beginTask("Collecting projects", ProgressMonitor.UNKNOWN);
+ List<Project.NameKey> names = new ArrayList<>();
+ for (Project.NameKey nameKey : projectCache.all()) {
+ names.add(nameKey);
+ }
+ progress.endTask();
+ return names;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/IndexedProjectQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/IndexedProjectQuery.java
new file mode 100644
index 0000000..41bff05
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/IndexedProjectQuery.java
@@ -0,0 +1,34 @@
+// 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.index.project;
+
+import com.google.gerrit.index.Index;
+import com.google.gerrit.index.IndexedQuery;
+import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.query.DataSource;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectData;
+
+public class IndexedProjectQuery extends IndexedQuery<Project.NameKey, ProjectData>
+ implements DataSource<ProjectData> {
+
+ public IndexedProjectQuery(
+ Index<Project.NameKey, ProjectData> index, Predicate<ProjectData> pred, QueryOptions opts)
+ throws QueryParseException {
+ super(index, pred, opts.convertForBackend());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectField.java
new file mode 100644
index 0000000..c4f8e9e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectField.java
@@ -0,0 +1,44 @@
+// 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.index.project;
+
+import static com.google.gerrit.index.FieldDef.exact;
+import static com.google.gerrit.index.FieldDef.fullText;
+import static com.google.gerrit.index.FieldDef.prefix;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.index.FieldDef;
+import com.google.gerrit.index.SchemaUtil;
+import com.google.gerrit.server.project.ProjectData;
+
+/** Index schema for projects. */
+public class ProjectField {
+
+ public static final FieldDef<ProjectData, String> NAME =
+ exact("name").stored().build(p -> p.getProject().getName());
+
+ public static final FieldDef<ProjectData, String> DESCRIPTION =
+ fullText("description").build(p -> p.getProject().getDescription());
+
+ public static final FieldDef<ProjectData, String> PARENT_NAME =
+ exact("parent_name").build(p -> p.getProject().getParentName());
+
+ public static final FieldDef<ProjectData, Iterable<String>> NAME_PART =
+ prefix("name_part").buildRepeatable(p -> SchemaUtil.getNameParts(p.getProject().getName()));
+
+ public static final FieldDef<ProjectData, Iterable<String>> ANCESTOR_NAME =
+ exact("ancestor_name")
+ .buildRepeatable(p -> Iterables.transform(p.getAncestors(), n -> n.get()));
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndex.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndex.java
new file mode 100644
index 0000000..5fbdf04
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndex.java
@@ -0,0 +1,33 @@
+// 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.index.project;
+
+import com.google.gerrit.index.Index;
+import com.google.gerrit.index.IndexDefinition;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.gerrit.server.query.project.ProjectPredicates;
+
+public interface ProjectIndex extends Index<Project.NameKey, ProjectData> {
+
+ public interface Factory
+ extends IndexDefinition.IndexFactory<Project.NameKey, ProjectData, ProjectIndex> {}
+
+ @Override
+ default Predicate<ProjectData> keyPredicate(Project.NameKey nameKey) {
+ return ProjectPredicates.name(nameKey);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexCollection.java
new file mode 100644
index 0000000..eeebfa1
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexCollection.java
@@ -0,0 +1,29 @@
+// 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.index.project;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.index.IndexCollection;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.inject.Singleton;
+
+@Singleton
+public class ProjectIndexCollection
+ extends IndexCollection<Project.NameKey, ProjectData, ProjectIndex> {
+
+ @VisibleForTesting
+ public ProjectIndexCollection() {}
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java
new file mode 100644
index 0000000..301f209
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java
@@ -0,0 +1,33 @@
+// 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.index.project;
+
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.index.IndexDefinition;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.inject.Inject;
+
+public class ProjectIndexDefinition
+ extends IndexDefinition<Project.NameKey, ProjectData, ProjectIndex> {
+
+ @Inject
+ ProjectIndexDefinition(
+ ProjectIndexCollection indexCollection,
+ ProjectIndex.Factory indexFactory,
+ @Nullable AllProjectsIndexer allProjectsIndexer) {
+ super(ProjectSchemaDefinitions.INSTANCE, indexCollection, indexFactory, allProjectsIndexer);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexRewriter.java
new file mode 100644
index 0000000..41d8820
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexRewriter.java
@@ -0,0 +1,43 @@
+// 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.index.project;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.gerrit.index.IndexRewriter;
+import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class ProjectIndexRewriter implements IndexRewriter<ProjectData> {
+ private final ProjectIndexCollection indexes;
+
+ @Inject
+ ProjectIndexRewriter(ProjectIndexCollection indexes) {
+ this.indexes = indexes;
+ }
+
+ @Override
+ public Predicate<ProjectData> rewrite(Predicate<ProjectData> in, QueryOptions opts)
+ throws QueryParseException {
+ ProjectIndex index = indexes.getSearchIndex();
+ checkNotNull(index, "no active search index configured for projects");
+ return new IndexedProjectQuery(index, in, opts);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexer.java
new file mode 100644
index 0000000..e8a8183
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexer.java
@@ -0,0 +1,28 @@
+// 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.index.project;
+
+import com.google.gerrit.reviewdb.client.Project;
+import java.io.IOException;
+
+public interface ProjectIndexer {
+
+ /**
+ * Synchronously index a project.
+ *
+ * @param nameKey name key of project to index.
+ */
+ void index(Project.NameKey nameKey) throws IOException;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
new file mode 100644
index 0000000..2a51f32
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
@@ -0,0 +1,86 @@
+// 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.index.project;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.events.ProjectIndexedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.index.Index;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+
+public class ProjectIndexerImpl implements ProjectIndexer {
+ public interface Factory {
+ ProjectIndexerImpl create(ProjectIndexCollection indexes);
+
+ ProjectIndexerImpl create(@Nullable ProjectIndex index);
+ }
+
+ private final ProjectCache projectCache;
+ private final DynamicSet<ProjectIndexedListener> indexedListener;
+ private final ProjectIndexCollection indexes;
+ private final ProjectIndex index;
+
+ @AssistedInject
+ ProjectIndexerImpl(
+ ProjectCache projectCache,
+ DynamicSet<ProjectIndexedListener> indexedListener,
+ @Assisted ProjectIndexCollection indexes) {
+ this.projectCache = projectCache;
+ this.indexedListener = indexedListener;
+ this.indexes = indexes;
+ this.index = null;
+ }
+
+ @AssistedInject
+ ProjectIndexerImpl(
+ ProjectCache projectCache,
+ DynamicSet<ProjectIndexedListener> indexedListener,
+ @Assisted ProjectIndex index) {
+ this.projectCache = projectCache;
+ this.indexedListener = indexedListener;
+ this.indexes = null;
+ this.index = index;
+ }
+
+ @Override
+ public void index(Project.NameKey nameKey) throws IOException {
+ for (Index<?, ProjectData> i : getWriteIndexes()) {
+ i.replace(projectCache.get(nameKey).toProjectData());
+ }
+ fireProjectIndexedEvent(nameKey.get());
+ }
+
+ private void fireProjectIndexedEvent(String name) {
+ for (ProjectIndexedListener listener : indexedListener) {
+ listener.onProjectIndexed(name);
+ }
+ }
+
+ private Collection<ProjectIndex> getWriteIndexes() {
+ if (indexes != null) {
+ return indexes.getWriteIndexes();
+ }
+
+ return index != null ? Collections.singleton(index) : ImmutableSet.of();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectSchemaDefinitions.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectSchemaDefinitions.java
new file mode 100644
index 0000000..ccece02
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectSchemaDefinitions.java
@@ -0,0 +1,38 @@
+// 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.index.project;
+
+import static com.google.gerrit.index.SchemaUtil.schema;
+
+import com.google.gerrit.index.Schema;
+import com.google.gerrit.index.SchemaDefinitions;
+import com.google.gerrit.server.project.ProjectData;
+
+public class ProjectSchemaDefinitions extends SchemaDefinitions<ProjectData> {
+
+ static final Schema<ProjectData> V1 =
+ schema(
+ ProjectField.NAME,
+ ProjectField.DESCRIPTION,
+ ProjectField.PARENT_NAME,
+ ProjectField.NAME_PART,
+ ProjectField.ANCESTOR_NAME);
+
+ public static final ProjectSchemaDefinitions INSTANCE = new ProjectSchemaDefinitions();
+
+ private ProjectSchemaDefinitions() {
+ super("projects", ProjectData.class);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
index 43813f8..777624a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -36,6 +36,7 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.server.OrmException;
import java.io.IOException;
@@ -86,6 +87,7 @@
private static final Retryer<RefUpdate.Result> RETRYER = retryerBuilder().build();
private final GitRepositoryManager repoManager;
+ private final GitReferenceUpdated gitRefUpdated;
private final Project.NameKey projectName;
private final String refName;
private final Seed seed;
@@ -103,16 +105,26 @@
public RepoSequence(
GitRepositoryManager repoManager,
+ GitReferenceUpdated gitRefUpdated,
Project.NameKey projectName,
String name,
Seed seed,
int batchSize) {
- this(repoManager, projectName, name, seed, batchSize, Runnables.doNothing(), RETRYER);
+ this(
+ repoManager,
+ gitRefUpdated,
+ projectName,
+ name,
+ seed,
+ batchSize,
+ Runnables.doNothing(),
+ RETRYER);
}
@VisibleForTesting
RepoSequence(
GitRepositoryManager repoManager,
+ GitReferenceUpdated gitRefUpdated,
Project.NameKey projectName,
String name,
Seed seed,
@@ -120,6 +132,7 @@
Runnable afterReadRef,
Retryer<RefUpdate.Result> retryer) {
this.repoManager = checkNotNull(repoManager, "repoManager");
+ this.gitRefUpdated = checkNotNull(gitRefUpdated, "gitRefUpdated");
this.projectName = checkNotNull(projectName, "projectName");
checkArgument(
@@ -213,11 +226,15 @@
}
private void checkResult(RefUpdate.Result result) throws OrmException {
- if (result != RefUpdate.Result.NEW && result != RefUpdate.Result.FORCED) {
+ if (!refUpdated(result)) {
throw new OrmException("failed to update " + refName + ": " + result);
}
}
+ private boolean refUpdated(RefUpdate.Result result) {
+ return result == RefUpdate.Result.NEW || result == RefUpdate.Result.FORCED;
+ }
+
private class TryAcquire implements Callable<RefUpdate.Result> {
private final Repository repo;
private final RevWalk rw;
@@ -275,7 +292,11 @@
}
ru.setNewObjectId(newId);
ru.setForceUpdate(true); // Required for non-commitish updates.
- return ru.update(rw);
+ RefUpdate.Result result = ru.update(rw);
+ if (refUpdated(result)) {
+ gitRefUpdated.fire(projectName, ru, null);
+ }
+ return result;
}
public static ReceiveCommand storeNew(ObjectInserter ins, String name, int val)
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 7913e82..f302177 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
@@ -51,6 +51,7 @@
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.GerritServerConfigProvider;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LockFailureException;
import com.google.gerrit.server.git.WorkQueue;
@@ -505,6 +506,7 @@
RepoSequence seq =
new RepoSequence(
repoManager,
+ GitReferenceUpdated.DISABLED,
allProjects,
Sequences.NAME_CHANGES,
// If sequenceGap is 0, this writes into the sequence ref the same ID that is returned
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java
index a2da580..266350f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java
@@ -15,32 +15,41 @@
package com.google.gerrit.server.plugins;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.plugins.DisablePlugin.Input;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
-@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@Singleton
public class DisablePlugin implements RestModifyView<PluginResource, Input> {
- public static class Input {}
private final PluginLoader loader;
+ private final Provider<IdentifiedUser> user;
+ private final PermissionBackend permissionBackend;
@Inject
- DisablePlugin(PluginLoader loader) {
+ DisablePlugin(
+ PluginLoader loader, Provider<IdentifiedUser> user, PermissionBackend permissionBackend) {
this.loader = loader;
+ this.user = user;
+ this.permissionBackend = permissionBackend;
}
@Override
- public PluginInfo apply(PluginResource resource, Input input) throws MethodNotAllowedException {
- if (!loader.isRemoteAdminEnabled()) {
- throw new MethodNotAllowedException("remote plugin administration is disabled");
+ public PluginInfo apply(PluginResource resource, Input input) throws RestApiException {
+ try {
+ permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+ } catch (PermissionBackendException e) {
+ throw new RestApiException("Could not check permission", e);
}
+ loader.checkRemoteAdminEnabled();
String name = resource.getName();
loader.disablePlugins(ImmutableSet.of(name));
return ListPlugins.toPluginInfo(loader.get(name));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java
index f29e36b..569bc39 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java
@@ -17,11 +17,11 @@
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.plugins.EnablePlugin.Input;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.PrintWriter;
@@ -30,7 +30,6 @@
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@Singleton
public class EnablePlugin implements RestModifyView<PluginResource, Input> {
- public static class Input {}
private final PluginLoader loader;
@@ -40,11 +39,8 @@
}
@Override
- public PluginInfo apply(PluginResource resource, Input input)
- throws ResourceConflictException, MethodNotAllowedException {
- if (!loader.isRemoteAdminEnabled()) {
- throw new MethodNotAllowedException("remote plugin administration is disabled");
- }
+ public PluginInfo apply(PluginResource resource, Input input) throws RestApiException {
+ loader.checkRemoteAdminEnabled();
String name = resource.getName();
try {
loader.enablePlugins(ImmutableSet.of(name));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
index 531e9ac..ee9099e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
@@ -19,8 +19,8 @@
import com.google.gerrit.extensions.common.InstallPluginInput;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
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.extensions.restapi.TopLevelResource;
import com.google.inject.Inject;
@@ -56,10 +56,8 @@
@Override
public Response<PluginInfo> apply(TopLevelResource resource, InstallPluginInput input)
- throws BadRequestException, MethodNotAllowedException, IOException {
- if (!loader.isRemoteAdminEnabled()) {
- throw new MethodNotAllowedException("remote installation is disabled");
- }
+ throws RestApiException, IOException {
+ loader.checkRemoteAdminEnabled();
try {
try (InputStream in = openStream(input)) {
String pluginName = loader.installPluginFromStream(name, in);
@@ -104,7 +102,7 @@
@Override
public Response<PluginInfo> apply(PluginResource resource, InstallPluginInput input)
- throws BadRequestException, MethodNotAllowedException, IOException {
+ throws RestApiException, IOException {
return install.get().setName(resource.getName()).apply(TopLevelResource.INSTANCE, input);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index d972087..954ea29 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -27,6 +27,7 @@
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.server.PluginUser;
import com.google.gerrit.server.cache.PersistentCacheFactory;
@@ -138,6 +139,12 @@
return remoteAdmin;
}
+ public void checkRemoteAdminEnabled() throws MethodNotAllowedException {
+ if (!remoteAdmin) {
+ throw new MethodNotAllowedException("remote plugin administration is disabled");
+ }
+ }
+
public Plugin get(String name) {
Plugin p = running.get(name);
if (p != null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java
index 768aa86..9dbc956 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java
@@ -17,8 +17,8 @@
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsCreate;
import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollection;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -67,11 +67,8 @@
}
@Override
- public InstallPlugin create(TopLevelResource parent, IdString id)
- throws ResourceNotFoundException, MethodNotAllowedException {
- if (!loader.isRemoteAdminEnabled()) {
- throw new MethodNotAllowedException("remote installation is disabled");
- }
+ public InstallPlugin create(TopLevelResource parent, IdString id) throws RestApiException {
+ loader.checkRemoteAdminEnabled();
return install.get().setName(id.get()).setCreated(true);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java
index 7b464bb..1134f50 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ReloadPlugin.java
@@ -17,10 +17,10 @@
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.plugins.ReloadPlugin.Input;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.PrintWriter;
@@ -29,7 +29,6 @@
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@Singleton
public class ReloadPlugin implements RestModifyView<PluginResource, Input> {
- public static class Input {}
private final PluginLoader loader;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
index d43a066..a9e8fd3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
@@ -211,8 +211,8 @@
DashboardInfo info = newDashboardInfo(refName, path);
info.project = project;
info.definingProject = definingProject.getName();
- String query = config.getString("dashboard", null, "title");
- info.title = replace(project, query == null ? info.path : query);
+ String title = config.getString("dashboard", null, "title");
+ info.title = replace(project, title == null ? info.path : title);
info.description = replace(project, config.getString("dashboard", null, "description"));
info.foreach = config.getString("dashboard", null, "foreach");
@@ -238,8 +238,8 @@
return info;
}
- private static String replace(String project, String query) {
- return query.replace("${project}", project);
+ private static String replace(String project, String input) {
+ return input == null ? input : input.replace("${project}", project);
}
private static String defaultOf(Project proj) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
index 8cd44d1..7c7d6af 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
@@ -16,6 +16,7 @@
import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -24,7 +25,6 @@
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.DeleteBranch.Input;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -34,7 +34,6 @@
@Singleton
public class DeleteBranch implements RestModifyView<BranchResource, Input> {
- public static class Input {}
private final Provider<InternalChangeQuery> queryProvider;
private final DeleteRef.Factory deleteRefFactory;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteTag.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteTag.java
index a05fa2e..234f1d5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteTag.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteTag.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -28,8 +29,7 @@
import java.io.IOException;
@Singleton
-public class DeleteTag implements RestModifyView<TagResource, DeleteTag.Input> {
- public static class Input {}
+public class DeleteTag implements RestModifyView<TagResource, Input> {
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java
index 0f71ac8..b68446f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java
@@ -18,6 +18,7 @@
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -48,7 +49,7 @@
return ctl;
}
- public void evict(Project project) {
+ public void evict(Project project) throws IOException {
projectCache.evict(project);
controls.remove(project.getNameKey());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
index 65c7315..63052bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
@@ -45,17 +45,27 @@
*/
ProjectState checkedGet(Project.NameKey projectName) throws IOException;
- /** Invalidate the cached information about the given project. */
- void evict(Project p);
+ /**
+ * Invalidate the cached information about the given project, and triggers reindexing for it
+ *
+ * @param p project that is being evicted
+ * @throws IOException thrown if the reindexing fails
+ */
+ void evict(Project p) throws IOException;
- /** Invalidate the cached information about the given project. */
- void evict(Project.NameKey p);
+ /**
+ * Invalidate the cached information about the given project, and triggers reindexing for it
+ *
+ * @param p the NameKey of the project that is being evicted
+ * @throws IOException thrown if the reindexing fails
+ */
+ void evict(Project.NameKey p) throws IOException;
/**
* Remove information about the given project from the cache. It will no longer be returned from
* {@link #all()}.
*/
- void remove(Project p);
+ void remove(Project p) throws IOException;
/** @return sorted iteration of projects. */
Iterable<Project.NameKey> all();
@@ -75,5 +85,5 @@
Iterable<Project.NameKey> byName(String prefix);
/** Notify the cache that a new project was constructed. */
- void onCreateProject(Project.NameKey newProjectName);
+ void onCreateProject(Project.NameKey newProjectName) throws IOException;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 6ee143c..2b31ce3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -28,8 +28,10 @@
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.index.project.ProjectIndexer;
import com.google.inject.Inject;
import com.google.inject.Module;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations;
@@ -82,6 +84,7 @@
private final LoadingCache<ListKey, SortedSet<Project.NameKey>> list;
private final Lock listLock;
private final ProjectCacheClock clock;
+ private final Provider<ProjectIndexer> indexer;
@Inject
ProjectCacheImpl(
@@ -89,13 +92,15 @@
final AllUsersName allUsersName,
@Named(CACHE_NAME) LoadingCache<String, ProjectState> byName,
@Named(CACHE_LIST) LoadingCache<ListKey, SortedSet<Project.NameKey>> list,
- ProjectCacheClock clock) {
+ ProjectCacheClock clock,
+ Provider<ProjectIndexer> indexer) {
this.allProjectsName = allProjectsName;
this.allUsersName = allUsersName;
this.byName = byName;
this.list = list;
this.listLock = new ReentrantLock(true /* fair */);
this.clock = clock;
+ this.indexer = indexer;
}
@Override
@@ -151,22 +156,20 @@
}
@Override
- public void evict(Project p) {
- if (p != null) {
- byName.invalidate(p.getNameKey().get());
- }
+ public void evict(Project p) throws IOException {
+ evict(p.getNameKey());
}
- /** Invalidate the cached information about the given project. */
@Override
- public void evict(Project.NameKey p) {
+ public void evict(Project.NameKey p) throws IOException {
if (p != null) {
byName.invalidate(p.get());
}
+ indexer.get().index(p);
}
@Override
- public void remove(Project p) {
+ public void remove(Project p) throws IOException {
listLock.lock();
try {
SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
@@ -181,7 +184,7 @@
}
@Override
- public void onCreateProject(Project.NameKey newProjectName) {
+ public void onCreateProject(Project.NameKey newProjectName) throws IOException {
listLock.lock();
try {
SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
@@ -192,6 +195,7 @@
} finally {
listLock.unlock();
}
+ indexer.get().index(newProjectName);
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index 7a7418c..1166970 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -390,6 +390,10 @@
}
}
+ public boolean canRead() {
+ return !isHidden() && allRefsAreVisible(Collections.emptySet());
+ }
+
ForProject asForProject() {
return new ForProjectImpl();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectData.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectData.java
new file mode 100644
index 0000000..407529d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectData.java
@@ -0,0 +1,36 @@
+// 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.common.collect.ImmutableList;
+import com.google.gerrit.reviewdb.client.Project;
+
+public class ProjectData {
+ private final Project project;
+ private final ImmutableList<Project.NameKey> ancestors;
+
+ public ProjectData(Project project, Iterable<Project.NameKey> ancestors) {
+ this.project = project;
+ this.ancestors = ImmutableList.copyOf(ancestors);
+ }
+
+ public Project getProject() {
+ return project;
+ }
+
+ public ImmutableList<Project.NameKey> getAncestors() {
+ return ancestors;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
index 3015164..bd6386c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -553,6 +553,10 @@
}
}
+ public ProjectData toProjectData() {
+ return new ProjectData(getProject(), parents().transform(s -> s.getProject().getNameKey()));
+ }
+
private String readFile(Path p) throws IOException {
return Files.exists(p) ? new String(Files.readAllBytes(p), UTF_8) : null;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
index e0741f0..2a79470 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
@@ -14,11 +14,14 @@
package com.google.gerrit.server.project;
+import com.google.common.collect.ListMultimap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsCreate;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.NeedsParams;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestCollection;
import com.google.gerrit.extensions.restapi.RestView;
@@ -38,24 +41,31 @@
@Singleton
public class ProjectsCollection
- implements RestCollection<TopLevelResource, ProjectResource>, AcceptsCreate<TopLevelResource> {
+ implements RestCollection<TopLevelResource, ProjectResource>,
+ AcceptsCreate<TopLevelResource>,
+ NeedsParams {
private final DynamicMap<RestView<ProjectResource>> views;
private final Provider<ListProjects> list;
+ private final Provider<QueryProjects> queryProjects;
private final ProjectControl.GenericFactory controlFactory;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
private final CreateProject.Factory createProjectFactory;
+ private boolean hasQuery;
+
@Inject
ProjectsCollection(
DynamicMap<RestView<ProjectResource>> views,
Provider<ListProjects> list,
+ Provider<QueryProjects> queryProjects,
ProjectControl.GenericFactory controlFactory,
PermissionBackend permissionBackend,
CreateProject.Factory factory,
Provider<CurrentUser> user) {
this.views = views;
this.list = list;
+ this.queryProjects = queryProjects;
this.controlFactory = controlFactory;
this.permissionBackend = permissionBackend;
this.user = user;
@@ -63,7 +73,16 @@
}
@Override
+ public void setParams(ListMultimap<String, String> params) throws BadRequestException {
+ // The --query option is defined in QueryProjects
+ this.hasQuery = params.containsKey("query");
+ }
+
+ @Override
public RestView<TopLevelResource> list() {
+ if (hasQuery) {
+ return queryProjects.get();
+ }
return list.get().setFormat(OutputFormat.JSON);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/QueryProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/QueryProjects.java
new file mode 100644
index 0000000..998bdb2
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/QueryProjects.java
@@ -0,0 +1,120 @@
+// 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.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.index.query.QueryResult;
+import com.google.gerrit.server.index.project.ProjectIndex;
+import com.google.gerrit.server.index.project.ProjectIndexCollection;
+import com.google.gerrit.server.query.project.ProjectQueryBuilder;
+import com.google.gerrit.server.query.project.ProjectQueryProcessor;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+import org.kohsuke.args4j.Option;
+
+public class QueryProjects implements RestReadView<TopLevelResource> {
+ private final ProjectIndexCollection indexes;
+ private final ProjectQueryBuilder queryBuilder;
+ private final ProjectQueryProcessor queryProcessor;
+ private final ProjectJson json;
+
+ private String query;
+ private int limit;
+ private int start;
+
+ @Option(
+ name = "--query",
+ aliases = {"-q"},
+ usage = "project query"
+ )
+ public void setQuery(String query) {
+ this.query = query;
+ }
+
+ @Option(
+ name = "--limit",
+ aliases = {"-n"},
+ metaVar = "CNT",
+ usage = "maximum number of projects to list"
+ )
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
+ @Option(
+ name = "--start",
+ aliases = {"-S"},
+ metaVar = "CNT",
+ usage = "number of projects to skip"
+ )
+ public void setStart(int start) {
+ this.start = start;
+ }
+
+ @Inject
+ protected QueryProjects(
+ ProjectIndexCollection indexes,
+ ProjectQueryBuilder queryBuilder,
+ ProjectQueryProcessor queryProcessor,
+ ProjectJson json) {
+ this.indexes = indexes;
+ this.queryBuilder = queryBuilder;
+ this.queryProcessor = queryProcessor;
+ this.json = json;
+ }
+
+ @Override
+ public List<ProjectInfo> apply(TopLevelResource resource)
+ throws BadRequestException, MethodNotAllowedException, OrmException {
+ if (Strings.isNullOrEmpty(query)) {
+ throw new BadRequestException("missing query field");
+ }
+
+ ProjectIndex searchIndex = indexes.getSearchIndex();
+ if (searchIndex == null) {
+ throw new MethodNotAllowedException("no project index");
+ }
+
+ if (start != 0) {
+ queryProcessor.setStart(start);
+ }
+
+ if (limit != 0) {
+ queryProcessor.setUserProvidedLimit(limit);
+ }
+
+ try {
+ QueryResult<ProjectData> result = queryProcessor.query(queryBuilder.parse(query));
+ List<ProjectData> pds = result.entities();
+
+ ArrayList<ProjectInfo> projectInfos = Lists.newArrayListWithCapacity(pds.size());
+ for (ProjectData pd : pds) {
+ projectInfos.add(json.format(pd.getProject()));
+ }
+ return projectInfos;
+ } catch (QueryParseException e) {
+ throw new BadRequestException(e.getMessage());
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 1ae579e..1f28dbd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -36,6 +36,7 @@
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.index.query.QueryRequiresAuthException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch;
@@ -391,23 +392,23 @@
return asUser(userFactory.create(otherId));
}
- IdentifiedUser getIdentifiedUser() throws QueryParseException {
+ IdentifiedUser getIdentifiedUser() throws QueryRequiresAuthException {
try {
CurrentUser u = getUser();
if (u.isIdentifiedUser()) {
return u.asIdentifiedUser();
}
- throw new QueryParseException(NotSignedInException.MESSAGE);
+ throw new QueryRequiresAuthException(NotSignedInException.MESSAGE);
} catch (ProvisionException e) {
- throw new QueryParseException(NotSignedInException.MESSAGE, e);
+ throw new QueryRequiresAuthException(NotSignedInException.MESSAGE, e);
}
}
- CurrentUser getUser() throws QueryParseException {
+ CurrentUser getUser() throws QueryRequiresAuthException {
try {
return self.get();
} catch (ProvisionException e) {
- throw new QueryParseException(NotSignedInException.MESSAGE, e);
+ throw new QueryRequiresAuthException(NotSignedInException.MESSAGE, e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
index 65de137..fadc853 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
@@ -24,6 +24,7 @@
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.index.query.QueryRequiresAuthException;
import com.google.gerrit.index.query.QueryResult;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gwtorm.server.OrmException;
@@ -32,11 +33,13 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.kohsuke.args4j.Option;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class QueryChanges implements RestReadView<TopLevelResource> {
+ private static final Logger log = LoggerFactory.getLogger(QueryChanges.class);
+
private final ChangeJson.Factory json;
private final ChangeQueryBuilder qb;
private final ChangeQueryProcessor imp;
@@ -106,15 +109,10 @@
List<List<ChangeInfo>> out;
try {
out = query();
+ } catch (QueryRequiresAuthException e) {
+ throw new AuthException("Must be signed-in to use this operator");
} catch (QueryParseException e) {
- // This is a hack to detect an operator that requires authentication.
- Pattern p =
- Pattern.compile("^Error in operator (.*:self|is:watched|is:owner|is:reviewer|has:.*)$");
- Matcher m = p.matcher(e.getMessage());
- if (m.matches()) {
- String op = m.group(1);
- throw new AuthException("Must be signed-in to use " + op);
- }
+ log.debug("Reject change query with 400 Bad Request: " + queries, e);
throw new BadRequestException(e.getMessage(), e);
}
return out.size() == 1 ? out.get(0) : out;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
new file mode 100644
index 0000000..20032ce
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
@@ -0,0 +1,48 @@
+// 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.query.project;
+
+import com.google.gerrit.index.query.IsVisibleToPredicate;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.index.IndexUtils;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.gerrit.server.query.account.AccountQueryBuilder;
+import com.google.gwtorm.server.OrmException;
+
+public class ProjectIsVisibleToPredicate extends IsVisibleToPredicate<ProjectData> {
+ protected final PermissionBackend permissionBackend;
+ protected final CurrentUser user;
+
+ public ProjectIsVisibleToPredicate(PermissionBackend permissionBackend, CurrentUser user) {
+ super(AccountQueryBuilder.FIELD_VISIBLETO, IndexUtils.describe(user));
+ this.permissionBackend = permissionBackend;
+ this.user = user;
+ }
+
+ @Override
+ public boolean match(ProjectData pd) throws OrmException {
+ return permissionBackend
+ .user(user)
+ .project(pd.getProject().getNameKey())
+ .testOrFalse(ProjectPermission.READ);
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectPredicates.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectPredicates.java
new file mode 100644
index 0000000..379c564
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectPredicates.java
@@ -0,0 +1,45 @@
+// 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.query.project;
+
+import com.google.gerrit.index.FieldDef;
+import com.google.gerrit.index.query.IndexPredicate;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.index.project.ProjectField;
+import com.google.gerrit.server.project.ProjectData;
+import java.util.Locale;
+
+public class ProjectPredicates {
+ public static Predicate<ProjectData> name(Project.NameKey nameKey) {
+ return new ProjectPredicate(ProjectField.NAME, nameKey.get());
+ }
+
+ public static Predicate<ProjectData> inname(String name) {
+ return new ProjectPredicate(ProjectField.NAME_PART, name.toLowerCase(Locale.US));
+ }
+
+ public static Predicate<ProjectData> description(String description) {
+ return new ProjectPredicate(ProjectField.DESCRIPTION, description);
+ }
+
+ static class ProjectPredicate extends IndexPredicate<ProjectData> {
+ ProjectPredicate(FieldDef<ProjectData, ?> def, String value) {
+ super(def, value);
+ }
+ }
+
+ private ProjectPredicates() {}
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
new file mode 100644
index 0000000..e9e9c0f
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
@@ -0,0 +1,83 @@
+// 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.query.project;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.primitives.Ints;
+import com.google.gerrit.index.query.LimitPredicate;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryBuilder;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.inject.Inject;
+import java.util.List;
+
+/** Parses a query string meant to be applied to project objects. */
+public class ProjectQueryBuilder extends QueryBuilder<ProjectData> {
+ public static final String FIELD_LIMIT = "limit";
+
+ private static final QueryBuilder.Definition<ProjectData, ProjectQueryBuilder> mydef =
+ new QueryBuilder.Definition<>(ProjectQueryBuilder.class);
+
+ @Inject
+ ProjectQueryBuilder() {
+ super(mydef);
+ }
+
+ @Operator
+ public Predicate<ProjectData> name(String name) {
+ return ProjectPredicates.name(new Project.NameKey(name));
+ }
+
+ @Operator
+ public Predicate<ProjectData> inname(String namePart) {
+ if (namePart.isEmpty()) {
+ return name(namePart);
+ }
+ return ProjectPredicates.inname(namePart);
+ }
+
+ @Operator
+ public Predicate<ProjectData> description(String description) throws QueryParseException {
+ if (Strings.isNullOrEmpty(description)) {
+ throw error("description operator requires a value");
+ }
+
+ return ProjectPredicates.description(description);
+ }
+
+ @Override
+ protected Predicate<ProjectData> defaultField(String query) throws QueryParseException {
+ // Adapt the capacity of this list when adding more default predicates.
+ List<Predicate<ProjectData>> preds = Lists.newArrayListWithCapacity(3);
+ preds.add(name(query));
+ preds.add(inname(query));
+ if (!Strings.isNullOrEmpty(query)) {
+ preds.add(description(query));
+ }
+ return Predicate.or(preds);
+ }
+
+ @Operator
+ public Predicate<ProjectData> limit(String query) throws QueryParseException {
+ Integer limit = Ints.tryParse(query);
+ if (limit == null) {
+ throw error("Invalid limit: " + query);
+ }
+ return new LimitPredicate<>(FIELD_LIMIT, limit);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryProcessor.java
new file mode 100644
index 0000000..1e181e5
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryProcessor.java
@@ -0,0 +1,79 @@
+// 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.query.project;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.query.project.ProjectQueryBuilder.FIELD_LIMIT;
+
+import com.google.gerrit.index.IndexConfig;
+import com.google.gerrit.index.query.AndSource;
+import com.google.gerrit.index.query.IndexPredicate;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryProcessor;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountLimits;
+import com.google.gerrit.server.index.project.ProjectIndexCollection;
+import com.google.gerrit.server.index.project.ProjectIndexRewriter;
+import com.google.gerrit.server.index.project.ProjectSchemaDefinitions;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+/**
+ * Query processor for the project index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
+public class ProjectQueryProcessor extends QueryProcessor<ProjectData> {
+ private final PermissionBackend permissionBackend;
+ private final Provider<CurrentUser> userProvider;
+
+ static {
+ // It is assumed that basic rewrites do not touch visibleto predicates.
+ checkState(
+ !ProjectIsVisibleToPredicate.class.isAssignableFrom(IndexPredicate.class),
+ "ProjectQueryProcessor assumes visibleto is not used by the index rewriter.");
+ }
+
+ @Inject
+ protected ProjectQueryProcessor(
+ Provider<CurrentUser> userProvider,
+ AccountLimits.Factory limitsFactory,
+ MetricMaker metricMaker,
+ IndexConfig indexConfig,
+ ProjectIndexCollection indexes,
+ ProjectIndexRewriter rewriter,
+ PermissionBackend permissionBackend) {
+ super(
+ metricMaker,
+ ProjectSchemaDefinitions.INSTANCE,
+ indexConfig,
+ indexes,
+ rewriter,
+ FIELD_LIMIT,
+ () -> limitsFactory.create(userProvider.get()).getQueryLimit());
+ this.permissionBackend = permissionBackend;
+ this.userProvider = userProvider;
+ }
+
+ @Override
+ protected Predicate<ProjectData> enforceVisibility(Predicate<ProjectData> pred) {
+ return new AndSource<>(
+ pred, new ProjectIsVisibleToPredicate(permissionBackend, userProvider.get()), start);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_144.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_144.java
index eaa97e4d5..d43b887 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_144.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_144.java
@@ -21,6 +21,7 @@
import com.google.gerrit.server.account.externalids.ExternalIdReader;
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.gwtorm.server.OrmException;
@@ -93,7 +94,18 @@
ExternalIdsUpdate.upsert(rw, ins, noteMap, extId);
}
- ExternalIdsUpdate.commit(repo, rw, ins, rev, noteMap, COMMIT_MSG, serverIdent, serverIdent);
+ ExternalIdsUpdate.commit(
+ allUsersName,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ COMMIT_MSG,
+ serverIdent,
+ serverIdent,
+ null,
+ GitReferenceUpdated.DISABLED);
}
} catch (IOException | ConfigInvalidException e) {
throw new OrmException("Failed to migrate external IDs to NoteDb", e);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_148.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_148.java
index 421e28d..47751cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_148.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_148.java
@@ -23,6 +23,7 @@
import com.google.gerrit.server.account.externalids.ExternalIdReader;
import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -84,7 +85,18 @@
}
}
if (dirty) {
- ExternalIdsUpdate.commit(repo, rw, ins, rev, noteMap, COMMIT_MSG, serverUser, serverUser);
+ ExternalIdsUpdate.commit(
+ allUsersName,
+ repo,
+ rw,
+ ins,
+ rev,
+ noteMap,
+ COMMIT_MSG,
+ serverUser,
+ serverUser,
+ null,
+ GitReferenceUpdated.DISABLED);
}
} catch (IOException e) {
throw new OrmException("Failed to update external IDs", e);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_155.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_155.java
index 64f60e3..2bb2a33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_155.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_155.java
@@ -17,6 +17,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.RepoSequence;
import com.google.gwtorm.server.OrmException;
@@ -42,7 +43,13 @@
@SuppressWarnings("deprecation")
RepoSequence.Seed accountSeed = () -> db.nextAccountId();
RepoSequence accountSeq =
- new RepoSequence(repoManager, allUsersName, Sequences.NAME_ACCOUNTS, accountSeed, 1);
+ new RepoSequence(
+ repoManager,
+ GitReferenceUpdated.DISABLED,
+ allUsersName,
+ Sequences.NAME_ACCOUNTS,
+ accountSeed,
+ 1);
// consume one account ID to ensure that the account sequence is initialized in NoteDb
accountSeq.next();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_161.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_161.java
index 407492d..febe80e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_161.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_161.java
@@ -16,6 +16,7 @@
import static java.util.stream.Collectors.toList;
+import com.google.common.primitives.Ints;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.StarredChangesUtil;
@@ -27,6 +28,9 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
@@ -54,19 +58,53 @@
try (Repository git = repoManager.openRepository(allUsersName);
RevWalk rw = new RevWalk(git)) {
BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
+ bru.setAllowNonFastForwards(true);
+
for (Ref ref : git.getRefDatabase().getRefs(RefNames.REFS_STARRED_CHANGES).values()) {
StarRef starRef = StarredChangesUtil.readLabels(git, ref.getName());
- if (starRef.labels().contains(MUTE_LABEL)) {
- ObjectId id =
- StarredChangesUtil.writeLabels(
- git,
- starRef
- .labels()
- .stream()
- .map(l -> l.equals(MUTE_LABEL) ? StarredChangesUtil.REVIEWED_LABEL : l)
- .collect(toList()));
- bru.addCommand(new ReceiveCommand(ObjectId.zeroId(), id, ref.getName()));
+
+ Set<Integer> mutedPatchSets =
+ StarredChangesUtil.getStarredPatchSets(starRef.labels(), MUTE_LABEL);
+ if (mutedPatchSets.isEmpty()) {
+ continue;
}
+
+ Set<Integer> reviewedPatchSets =
+ StarredChangesUtil.getStarredPatchSets(
+ starRef.labels(), StarredChangesUtil.REVIEWED_LABEL);
+ Set<Integer> unreviewedPatchSets =
+ StarredChangesUtil.getStarredPatchSets(
+ starRef.labels(), StarredChangesUtil.UNREVIEWED_LABEL);
+
+ List<String> newLabels =
+ starRef
+ .labels()
+ .stream()
+ .map(
+ l -> {
+ if (l.startsWith(MUTE_LABEL)) {
+ Integer mutedPatchSet = Ints.tryParse(l.substring(MUTE_LABEL.length() + 1));
+ if (mutedPatchSet == null) {
+ // unexpected format of mute label, must be a label that was manually
+ // set, just leave it alone
+ return l;
+ }
+ if (!reviewedPatchSets.contains(mutedPatchSet)
+ && !unreviewedPatchSets.contains(mutedPatchSet)) {
+ // convert mute label to reviewed label
+ return StarredChangesUtil.REVIEWED_LABEL + "/" + mutedPatchSet;
+ }
+ // else patch set is muted but has either reviewed or unreviewed label
+ // -> just drop the mute label
+ return null;
+ }
+ return l;
+ })
+ .filter(Objects::nonNull)
+ .collect(toList());
+
+ ObjectId id = StarredChangesUtil.writeLabels(git, newLabels);
+ bru.addCommand(new ReceiveCommand(ref.getTarget().getObjectId(), id, ref.getName()));
}
bru.execute(rw, new TextProgressMonitor());
} catch (IOException | IllegalLabelException ex) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java
index 2088409..e757d77 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/MagicBranch.java
@@ -27,21 +27,27 @@
private static final Logger log = LoggerFactory.getLogger(MagicBranch.class);
public static final String NEW_CHANGE = "refs/for/";
- // TODO: remove after 'repo' supports private/wip changes.
+ // TODO(xchangcheng): remove after 'repo' supports private/wip changes.
public static final String NEW_DRAFT_CHANGE = "refs/drafts/";
+ // TODO(xchangcheng): remove after migrating tools which are using this magic branch.
+ public static final String NEW_PUBLISH_CHANGE = "refs/publish/";
/** Extracts the destination from a ref name */
public static String getDestBranchName(String refName) {
String magicBranch = NEW_CHANGE;
if (refName.startsWith(NEW_DRAFT_CHANGE)) {
magicBranch = NEW_DRAFT_CHANGE;
+ } else if (refName.startsWith(NEW_PUBLISH_CHANGE)) {
+ magicBranch = NEW_PUBLISH_CHANGE;
}
return refName.substring(magicBranch.length());
}
/** Checks if the supplied ref name is a magic branch */
public static boolean isMagicBranch(String refName) {
- return refName.startsWith(NEW_DRAFT_CHANGE) || refName.startsWith(NEW_CHANGE);
+ return refName.startsWith(NEW_DRAFT_CHANGE)
+ || refName.startsWith(NEW_PUBLISH_CHANGE)
+ || refName.startsWith(NEW_CHANGE);
}
/** Returns the ref name prefix for a magic branch, {@code null} if the branch is not magic */
@@ -49,6 +55,9 @@
if (refName.startsWith(NEW_DRAFT_CHANGE)) {
return NEW_DRAFT_CHANGE;
}
+ if (refName.startsWith(NEW_PUBLISH_CHANGE)) {
+ return NEW_PUBLISH_CHANGE;
+ }
if (refName.startsWith(NEW_CHANGE)) {
return NEW_CHANGE;
}
@@ -72,6 +81,11 @@
if (result != Capable.OK) {
return result;
}
+ result = checkMagicBranchRef(NEW_PUBLISH_CHANGE, repo, project);
+ if (result != Capable.OK) {
+ return result;
+ }
+
return Capable.OK;
}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
index fb8ff78..75d940f 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
@@ -21,7 +21,7 @@
* @param email
* @param fromName
*/
-{template .AbandonedHtml kind="html"}
+{template .AbandonedHtml}
<p>
{$fromName} <strong>abandoned</strong> this change.
</p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
index 21161ea..712abc7 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
@@ -19,7 +19,7 @@
/**
* @param email
*/
-{template .AddKeyHtml kind="html"}
+{template .AddKeyHtml}
<p>
One or more new {$email.keyType} keys have been added to Gerrit Code Review
at {$email.gerritHost}:
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
index dea6724..99263e8 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
@@ -20,7 +20,7 @@
* @param change
* @param email
*/
-{template .ChangeFooterHtml kind="html"}
+{template .ChangeFooterHtml}
{if $email.changeUrl or $email.settingsUrl}
<p>
{if $email.changeUrl}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy
index c54f926..033c1b1 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy
@@ -16,5 +16,5 @@
{namespace com.google.gerrit.server.mail.template}
-{template .CommentFooterHtml kind="html"}
+{template .CommentFooterHtml}
{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy
index 9b96d69..6917736 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy
@@ -24,7 +24,7 @@
* @param patchSet
* @param patchSetCommentBlocks
*/
-{template .CommentHtml kind="html"}
+{template .CommentHtml}
{let $commentHeaderStyle kind="css"}
margin-bottom: 4px;
{/let}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
index 74e5ee5..f73e387 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
@@ -20,7 +20,7 @@
* @param email
* @param fromName
*/
-{template .DeleteReviewerHtml kind="html"}
+{template .DeleteReviewerHtml}
<p>
{$fromName}{sp}
<strong>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
index 06f5456..cb8162d 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
@@ -21,7 +21,7 @@
* @param email
* @param fromName
*/
-{template .DeleteVoteHtml kind="html"}
+{template .DeleteVoteHtml}
<p>
{$fromName} <strong>removed a vote</strong> from this change.
</p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy
index d9f13ce..22929d1 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy
@@ -19,7 +19,7 @@
/**
* @param footers
*/
-{template .FooterHtml kind="html"}
+{template .FooterHtml}
{\n}
{\n}
{foreach $footer in $footers}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy
index 85b56ec..4710d8c 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy
@@ -16,5 +16,5 @@
{namespace com.google.gerrit.server.mail.template}
-{template .HeaderHtml kind="html"}
+{template .HeaderHtml}
{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy
index 08d37cc..b11c5e5 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy
@@ -21,7 +21,7 @@
* @param email
* @param fromName
*/
-{template .MergedHtml kind="html"}
+{template .MergedHtml}
<p>
{$fromName} <strong>merged</strong> this change.
</p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
index 676f019..16b0df4 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
@@ -24,7 +24,7 @@
* @param patchSet
* @param projectName
*/
-{template .NewChangeHtml kind="html"}
+{template .NewChangeHtml}
<p>
{if $email.reviewerNames}
{$fromName} would like{sp}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy
index c1ac5b6..5840223 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy
@@ -24,7 +24,7 @@
* Private template to generate "View Change" buttons.
* @param email
*/
-{template .ViewChangeButton kind="html"}
+{template .ViewChangeButton}
<a href="{$email.changeUrl}">View Change</a>
{/template}
@@ -32,7 +32,7 @@
* Private template to render PRE block with consistent font-sizing.
* @param content
*/
-{template .Pre kind="html"}
+{template .Pre}
{let $preStyle kind="css"}
font-family: monospace,monospace; // Use this to avoid browsers scaling down
// monospace text.
@@ -56,7 +56,7 @@
*
* @param content
*/
-{template .WikiFormat kind="html"}
+{template .WikiFormat}
{let $blockquoteStyle kind="css"}
border-left: 1px solid #aaa;
margin: 10px 0;
@@ -90,7 +90,7 @@
/**
* @param diffLines
*/
-{template .UnifiedDiff kind="html"}
+{template .UnifiedDiff}
{let $addStyle kind="css"}
color: hsl(120, 100%, 40%);
{/let}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
index 221a4e6..e618bef 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
@@ -24,7 +24,7 @@
* @param patchSet
* @param projectName
*/
-{template .ReplacePatchSetHtml kind="html"}
+{template .ReplacePatchSetHtml}
<p>
{$fromName} <strong>uploaded patch set #{$patchSet.patchSetId}</strong>{sp}
to{sp}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy
index fdc68b0..bb856ac 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy
@@ -20,7 +20,7 @@
* @param email
* @param fromName
*/
-{template .RestoredHtml kind="html"}
+{template .RestoredHtml}
<p>
{$fromName} <strong>restored</strong> this change.
</p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy
index 479eae1..63ad6f0 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy
@@ -20,7 +20,7 @@
* @param email
* @param fromName
*/
-{template .RevertedHtml kind="html"}
+{template .RevertedHtml}
<p>
{$fromName} <strong>reverted</strong> this change.
</p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
index d057ba3..dbd3fae 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
@@ -23,7 +23,7 @@
* @param patchSet
* @param projectName
*/
-{template .SetAssigneeHtml kind="html"}
+{template .SetAssigneeHtml}
<p>
{$fromName} has <strong>assigned</strong> a change to{sp}
{$email.assigneeName}.{sp}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/RepoSequenceTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/RepoSequenceTest.java
index 66ccdad..76be4569 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/RepoSequenceTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/RepoSequenceTest.java
@@ -25,6 +25,7 @@
import com.google.common.util.concurrent.Runnables;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.testutil.InMemoryRepositoryManager;
import com.google.gwtorm.server.OrmException;
import java.io.IOException;
@@ -264,7 +265,14 @@
Runnable afterReadRef,
Retryer<RefUpdate.Result> retryer) {
return new RepoSequence(
- repoManager, project, name, () -> start, batchSize, afterReadRef, retryer);
+ repoManager,
+ GitReferenceUpdated.DISABLED,
+ project,
+ name,
+ () -> start,
+ batchSize,
+ afterReadRef,
+ retryer);
}
private ObjectId writeBlob(String sequenceName, String value) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
new file mode 100644
index 0000000..8804b96
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
@@ -0,0 +1,372 @@
+// 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.query.project;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.base.CharMatcher;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.projects.ProjectInput;
+import com.google.gerrit.extensions.api.projects.Projects.QueryRequest;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.query.account.InternalAccountQuery;
+import com.google.gerrit.server.schema.SchemaCreator;
+import com.google.gerrit.server.util.ManualRequestContext;
+import com.google.gerrit.server.util.OneOffRequestContext;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gerrit.testutil.ConfigSuite;
+import com.google.gerrit.testutil.GerritServerTests;
+import com.google.gerrit.testutil.InMemoryDatabase;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore
+public abstract class AbstractQueryProjectsTest extends GerritServerTests {
+ @ConfigSuite.Default
+ public static Config defaultConfig() {
+ Config cfg = new Config();
+ cfg.setInt("index", null, "maxPages", 10);
+ return cfg;
+ }
+
+ @Inject protected Accounts accounts;
+
+ @Inject protected AccountsUpdate.Server accountsUpdate;
+
+ @Inject protected AccountCache accountCache;
+
+ @Inject protected AccountManager accountManager;
+
+ @Inject protected GerritApi gApi;
+
+ @Inject protected IdentifiedUser.GenericFactory userFactory;
+
+ @Inject private Provider<AnonymousUser> anonymousUser;
+
+ @Inject protected InMemoryDatabase schemaFactory;
+
+ @Inject protected SchemaCreator schemaCreator;
+
+ @Inject protected ThreadLocalRequestContext requestContext;
+
+ @Inject protected OneOffRequestContext oneOffRequestContext;
+
+ @Inject protected InternalAccountQuery internalAccountQuery;
+
+ @Inject protected AllProjectsName allProjects;
+
+ protected LifecycleManager lifecycle;
+ protected Injector injector;
+ protected ReviewDb db;
+ protected AccountInfo currentUserInfo;
+ protected CurrentUser user;
+
+ protected abstract Injector createInjector();
+
+ @Before
+ public void setUpInjector() throws Exception {
+ lifecycle = new LifecycleManager();
+ injector = createInjector();
+ lifecycle.add(injector);
+ injector.injectMembers(this);
+ lifecycle.start();
+ setUpDatabase();
+ }
+
+ protected void setUpDatabase() throws Exception {
+ db = schemaFactory.open();
+ schemaCreator.create(db);
+
+ Account.Id userId = createAccount("user", "User", "user@example.com", true);
+ user = userFactory.create(userId);
+ requestContext.setContext(newRequestContext(userId));
+ currentUserInfo = gApi.accounts().id(userId.get()).get();
+ }
+
+ protected RequestContext newRequestContext(Account.Id requestUserId) {
+ final CurrentUser requestUser = userFactory.create(requestUserId);
+ return new RequestContext() {
+ @Override
+ public CurrentUser getUser() {
+ return requestUser;
+ }
+
+ @Override
+ public Provider<ReviewDb> getReviewDbProvider() {
+ return Providers.of(db);
+ }
+ };
+ }
+
+ protected void setAnonymous() {
+ requestContext.setContext(
+ new RequestContext() {
+ @Override
+ public CurrentUser getUser() {
+ return anonymousUser.get();
+ }
+
+ @Override
+ public Provider<ReviewDb> getReviewDbProvider() {
+ return Providers.of(db);
+ }
+ });
+ }
+
+ @After
+ public void tearDownInjector() {
+ if (lifecycle != null) {
+ lifecycle.stop();
+ }
+ requestContext.setContext(null);
+ if (db != null) {
+ db.close();
+ }
+ InMemoryDatabase.drop(schemaFactory);
+ }
+
+ @Test
+ public void byName() throws Exception {
+ assertQuery("name:project");
+ assertQuery("name:non-existing");
+
+ ProjectInfo project = createProject(name("project"));
+
+ assertQuery("name:" + project.name, project);
+
+ // only exact match
+ ProjectInfo projectWithHyphen = createProject(name("project-with-hyphen"));
+ createProject(name("project-no-match-with-hyphen"));
+ assertQuery("name:" + projectWithHyphen.name, projectWithHyphen);
+ }
+
+ @Test
+ public void byInname() throws Exception {
+ String namePart = getSanitizedMethodName();
+ namePart = CharMatcher.is('_').removeFrom(namePart);
+
+ ProjectInfo project1 = createProject(name("project-" + namePart));
+ ProjectInfo project2 = createProject(name("project-" + namePart + "-2"));
+ ProjectInfo project3 = createProject(name("project-" + namePart + "3"));
+
+ assertQuery("inname:" + namePart, project1, project2, project3);
+ assertQuery("inname:" + namePart.toUpperCase(Locale.US), project1, project2, project3);
+ assertQuery("inname:" + namePart.toLowerCase(Locale.US), project1, project2, project3);
+ }
+
+ @Test
+ public void byDescription() throws Exception {
+ ProjectInfo project1 =
+ createProjectWithDescription(name("project1"), "This is a test project.");
+ ProjectInfo project2 = createProjectWithDescription(name("project2"), "ANOTHER TEST PROJECT.");
+ createProjectWithDescription(name("project3"), "Maintainers of project foo.");
+ assertQuery("description:test", project1, project2);
+
+ assertQuery("description:non-existing");
+
+ exception.expect(BadRequestException.class);
+ exception.expectMessage("description operator requires a value");
+ assertQuery("description:\"\"");
+ }
+
+ @Test
+ public void byDefaultField() throws Exception {
+ ProjectInfo project1 = createProject(name("foo-project"));
+ ProjectInfo project2 = createProject(name("project2"));
+ ProjectInfo project3 =
+ createProjectWithDescription(
+ name("project3"),
+ "decription that contains foo and the UUID of project2: " + project2.id);
+
+ assertQuery("non-existing");
+ assertQuery("foo", project1, project3);
+ assertQuery(project2.id, project2, project3);
+ }
+
+ @Test
+ public void withLimit() throws Exception {
+ ProjectInfo project1 = createProject(name("project1"));
+ ProjectInfo project2 = createProject(name("project2"));
+ ProjectInfo project3 = createProject(name("project3"));
+
+ String query =
+ "name:" + project1.name + " OR name:" + project2.name + " OR name:" + project3.name;
+ List<ProjectInfo> result = assertQuery(query, project1, project2, project3);
+
+ result = assertQuery(newQuery(query).withLimit(2), result.subList(0, 2));
+ }
+
+ @Test
+ public void withStart() throws Exception {
+ ProjectInfo project1 = createProject(name("project1"));
+ ProjectInfo project2 = createProject(name("project2"));
+ ProjectInfo project3 = createProject(name("project3"));
+
+ String query =
+ "name:" + project1.name + " OR name:" + project2.name + " OR name:" + project3.name;
+ List<ProjectInfo> result = assertQuery(query, project1, project2, project3);
+
+ assertQuery(newQuery(query).withStart(1), result.subList(1, 3));
+ }
+
+ @Test
+ public void asAnonymous() throws Exception {
+ ProjectInfo project = createProject(name("project"));
+
+ setAnonymous();
+ assertQuery("name:" + project.name);
+ }
+
+ private Account.Id createAccount(String username, String fullName, String email, boolean active)
+ throws Exception {
+ try (ManualRequestContext ctx = oneOffRequestContext.open()) {
+ Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId();
+ if (email != null) {
+ accountManager.link(id, AuthRequest.forEmail(email));
+ }
+ accountsUpdate
+ .create()
+ .update(
+ id,
+ a -> {
+ a.setFullName(fullName);
+ a.setPreferredEmail(email);
+ a.setActive(active);
+ });
+ return id;
+ }
+ }
+
+ protected ProjectInfo createProject(String name) throws Exception {
+ ProjectInput in = new ProjectInput();
+ in.name = name;
+ return gApi.projects().create(in).get();
+ }
+
+ protected ProjectInfo createProjectWithDescription(String name, String description)
+ throws Exception {
+ ProjectInput in = new ProjectInput();
+ in.name = name;
+ in.description = description;
+ return gApi.projects().create(in).get();
+ }
+
+ protected ProjectInfo getProject(Project.NameKey nameKey) throws Exception {
+ return gApi.projects().name(nameKey.get()).get();
+ }
+
+ protected List<ProjectInfo> assertQuery(Object query, ProjectInfo... projects) throws Exception {
+ return assertQuery(newQuery(query), projects);
+ }
+
+ protected List<ProjectInfo> assertQuery(QueryRequest query, ProjectInfo... projects)
+ throws Exception {
+ return assertQuery(query, Arrays.asList(projects));
+ }
+
+ protected List<ProjectInfo> assertQuery(QueryRequest query, List<ProjectInfo> projects)
+ throws Exception {
+ List<ProjectInfo> result = query.get();
+ Iterable<String> names = names(result);
+ assertThat(names)
+ .named(format(query, result, projects))
+ .containsExactlyElementsIn(names(projects));
+ return result;
+ }
+
+ protected QueryRequest newQuery(Object query) {
+ return gApi.projects().query(query.toString());
+ }
+
+ protected String format(
+ QueryRequest query, List<ProjectInfo> actualProjects, List<ProjectInfo> expectedProjects) {
+ StringBuilder b = new StringBuilder();
+ b.append("query '").append(query.getQuery()).append("' with expected projects ");
+ b.append(format(expectedProjects));
+ b.append(" and result ");
+ b.append(format(actualProjects));
+ return b.toString();
+ }
+
+ protected String format(Iterable<ProjectInfo> projects) {
+ StringBuilder b = new StringBuilder();
+ b.append("[");
+ Iterator<ProjectInfo> it = projects.iterator();
+ while (it.hasNext()) {
+ ProjectInfo p = it.next();
+ b.append("{")
+ .append(p.id)
+ .append(", ")
+ .append("name=")
+ .append(p.name)
+ .append(", ")
+ .append("parent=")
+ .append(p.parent)
+ .append(", ")
+ .append("description=")
+ .append(p.description)
+ .append("}");
+ if (it.hasNext()) {
+ b.append(", ");
+ }
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ protected static Iterable<String> names(ProjectInfo... projects) {
+ return names(Arrays.asList(projects));
+ }
+
+ protected static Iterable<String> names(List<ProjectInfo> projects) {
+ return projects.stream().map(p -> p.name).collect(toList());
+ }
+
+ protected String name(String name) {
+ if (name == null) {
+ return null;
+ }
+
+ return name + "_" + getSanitizedMethodName();
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
new file mode 100644
index 0000000..4a09d87
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
@@ -0,0 +1,44 @@
+// 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.query.project;
+
+import com.google.gerrit.server.index.project.ProjectSchemaDefinitions;
+import com.google.gerrit.testutil.ConfigSuite;
+import com.google.gerrit.testutil.InMemoryModule;
+import com.google.gerrit.testutil.IndexVersions;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.lib.Config;
+
+public class LuceneQueryProjectsTest extends AbstractQueryProjectsTest {
+ @ConfigSuite.Configs
+ public static Map<String, Config> againstPreviousIndexVersion() {
+ // the current schema version is already tested by the inherited default config suite
+ List<Integer> schemaVersions =
+ IndexVersions.getWithoutLatest(
+ com.google.gerrit.server.index.project.ProjectSchemaDefinitions.INSTANCE);
+ return IndexVersions.asConfigMap(
+ ProjectSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
+ }
+
+ @Override
+ protected Injector createInjector() {
+ Config luceneConfig = new Config(config);
+ InMemoryModule.setDefaults(luceneConfig);
+ return Guice.createInjector(new InMemoryModule(luceneConfig, notesMigration));
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
index 0d7fa24..6649fcb 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
@@ -174,7 +174,13 @@
err.append("error: ").append(msg).append("\n");
}
- projectCache.evict(nameKey);
+ try {
+ projectCache.evict(nameKey);
+ } catch (IOException e) {
+ final String msg = "Cannot reindex project: " + name;
+ log.error(msg, e);
+ err.append("error: ").append(msg).append("\n");
+ }
}
if (err.length() > 0) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
index 821257c..bb33dea 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -14,6 +14,7 @@
package com.google.gerrit.sshd.commands;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.Index;
@@ -55,7 +56,7 @@
boolean ok = true;
for (ChangeResource rsrc : changes.values()) {
try {
- index.apply(rsrc, new Index.Input());
+ index.apply(rsrc, new Input());
} catch (Exception e) {
ok = false;
writeError(
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
new file mode 100644
index 0000000..7e32615
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
@@ -0,0 +1,36 @@
+// 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.sshd.commands;
+
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.server.plugins.PluginLoader;
+import com.google.gerrit.sshd.SshCommand;
+import com.google.inject.Inject;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+public abstract class PluginAdminSshCommand extends SshCommand {
+ @Inject protected PluginLoader loader;
+
+ abstract void doRun() throws UnloggedFailure;
+
+ @Override
+ protected final void run() throws UnloggedFailure {
+ if (!loader.isRemoteAdminEnabled()) {
+ throw die("remote plugin administration is disabled");
+ }
+ doRun();
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java
index d7c8f3a..baaf715 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java
@@ -17,29 +17,18 @@
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.collect.Sets;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.plugins.PluginInstallException;
-import com.google.gerrit.server.plugins.PluginLoader;
import com.google.gerrit.sshd.CommandMetaData;
-import com.google.gerrit.sshd.SshCommand;
-import com.google.inject.Inject;
import java.util.List;
import org.kohsuke.args4j.Argument;
-@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@CommandMetaData(name = "enable", description = "Enable plugins", runsAt = MASTER_OR_SLAVE)
-final class PluginEnableCommand extends SshCommand {
+final class PluginEnableCommand extends PluginAdminSshCommand {
@Argument(index = 0, metaVar = "NAME", required = true, usage = "plugin(s) to enable")
List<String> names;
- @Inject private PluginLoader loader;
-
@Override
- protected void run() throws UnloggedFailure {
- if (!loader.isRemoteAdminEnabled()) {
- throw die("remote plugin administration is disabled");
- }
+ protected void doRun() throws UnloggedFailure {
if (names != null && !names.isEmpty()) {
try {
loader.enablePlugins(Sets.newHashSet(names));
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
index 820052c..337eadb 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
@@ -17,13 +17,8 @@
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.base.Strings;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.plugins.PluginInstallException;
-import com.google.gerrit.server.plugins.PluginLoader;
import com.google.gerrit.sshd.CommandMetaData;
-import com.google.gerrit.sshd.SshCommand;
-import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -33,9 +28,8 @@
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
-@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@CommandMetaData(name = "install", description = "Install/Add a plugin", runsAt = MASTER_OR_SLAVE)
-final class PluginInstallCommand extends SshCommand {
+final class PluginInstallCommand extends PluginAdminSshCommand {
@Option(
name = "--name",
aliases = {"-n"},
@@ -51,14 +45,9 @@
@Argument(index = 0, metaVar = "-|URL", usage = "JAR to load")
private String source;
- @Inject private PluginLoader loader;
-
@SuppressWarnings("resource")
@Override
- protected void run() throws UnloggedFailure {
- if (!loader.isRemoteAdminEnabled()) {
- throw die("remote installation is disabled");
- }
+ protected void doRun() throws UnloggedFailure {
if (Strings.isNullOrEmpty(source)) {
throw die("Argument \"-|URL\" is required");
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java
index 0f2c912..86a74d1 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java
@@ -16,30 +16,19 @@
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.plugins.InvalidPluginException;
import com.google.gerrit.server.plugins.PluginInstallException;
-import com.google.gerrit.server.plugins.PluginLoader;
import com.google.gerrit.sshd.CommandMetaData;
-import com.google.gerrit.sshd.SshCommand;
-import com.google.inject.Inject;
import java.util.List;
import org.kohsuke.args4j.Argument;
-@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@CommandMetaData(name = "reload", description = "Reload/Restart plugins", runsAt = MASTER_OR_SLAVE)
-final class PluginReloadCommand extends SshCommand {
+final class PluginReloadCommand extends PluginAdminSshCommand {
@Argument(index = 0, metaVar = "NAME", usage = "plugins to reload/restart")
private List<String> names;
- @Inject private PluginLoader loader;
-
@Override
- protected void run() throws UnloggedFailure {
- if (!loader.isRemoteAdminEnabled()) {
- throw die("remote plugin administration is disabled");
- }
+ protected void doRun() throws UnloggedFailure {
if (names == null || names.isEmpty()) {
loader.rescan();
} else {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java
index 8a38739..0119349b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java
@@ -17,28 +17,17 @@
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.collect.Sets;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
-import com.google.gerrit.server.plugins.PluginLoader;
import com.google.gerrit.sshd.CommandMetaData;
-import com.google.gerrit.sshd.SshCommand;
-import com.google.inject.Inject;
import java.util.List;
import org.kohsuke.args4j.Argument;
-@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@CommandMetaData(name = "remove", description = "Disable plugins", runsAt = MASTER_OR_SLAVE)
-final class PluginRemoveCommand extends SshCommand {
+final class PluginRemoveCommand extends PluginAdminSshCommand {
@Argument(index = 0, metaVar = "NAME", required = true, usage = "plugin to remove")
List<String> names;
- @Inject private PluginLoader loader;
-
@Override
- protected void run() throws UnloggedFailure {
- if (!loader.isRemoteAdminEnabled()) {
- throw die("remote plugin administration is disabled");
- }
+ protected void doRun() throws UnloggedFailure {
if (names != null && !names.isEmpty()) {
loader.disablePlugins(Sets.newHashSet(names));
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
index 6ec3a28..74dcc12 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -14,6 +14,7 @@
package com.google.gerrit.sshd.commands;
+import com.google.gerrit.extensions.common.NameInput;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -48,7 +49,7 @@
protected void run() throws Failure {
try {
GroupResource rsrc = groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName));
- PutName.Input input = new PutName.Input();
+ NameInput input = new NameInput();
input.name = newGroupName;
putName.apply(rsrc, input);
} catch (RestApiException | OrmException | IOException e) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 033b4c6..656d377 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -23,7 +23,11 @@
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.common.EmailInfo;
+import com.google.gerrit.extensions.common.HttpPasswordInput;
+import com.google.gerrit.extensions.common.Input;
+import com.google.gerrit.extensions.common.NameInput;
import com.google.gerrit.extensions.common.SshKeyInfo;
+import com.google.gerrit.extensions.common.SshKeyInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -193,13 +197,13 @@
}
if (fullName != null) {
- PutName.Input in = new PutName.Input();
+ NameInput in = new NameInput();
in.name = fullName;
putName.apply(rsrc, in);
}
if (httpPassword != null || clearHttpPassword) {
- PutHttpPassword.Input in = new PutHttpPassword.Input();
+ HttpPasswordInput in = new HttpPasswordInput();
in.httpPassword = httpPassword;
putHttpPassword.apply(rsrc, in);
}
@@ -232,7 +236,7 @@
throws RestApiException, OrmException, IOException, ConfigInvalidException,
PermissionBackendException {
for (String sshKey : sshKeys) {
- AddSshKey.Input in = new AddSshKey.Input();
+ SshKeyInput in = new SshKeyInput();
in.raw = RawInputUtil.create(sshKey.getBytes(UTF_8), "plain/text");
addSshKey.apply(rsrc, in);
}
@@ -284,10 +288,10 @@
if (email.equals("ALL")) {
List<EmailInfo> emails = getEmails.apply(rsrc);
for (EmailInfo e : emails) {
- deleteEmail.apply(new AccountResource.Email(user, e.email), new DeleteEmail.Input());
+ deleteEmail.apply(new AccountResource.Email(user, e.email), new Input());
}
} else {
- deleteEmail.apply(new AccountResource.Email(user, email), new DeleteEmail.Input());
+ deleteEmail.apply(new AccountResource.Email(user, email), new Input());
}
}
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index 63761f2..1d964b8 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,12 +1,12 @@
load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_LOCAL", "MAVEN_CENTRAL", "maven_jar")
-_JGIT_VERS = "4.8.0.201706111038-r.71-g45da0fc6f"
+_JGIT_VERS = "4.9.0.201710071750-r"
-_DOC_VERS = "4.8.0.201706111038-r" # Set to _JGIT_VERS unless using a snapshot
+_DOC_VERS = _JGIT_VERS # Set to _JGIT_VERS unless using a snapshot
JGIT_DOC_URL = "http://download.eclipse.org/jgit/site/" + _DOC_VERS + "/apidocs"
-_JGIT_REPO = GERRIT # Leave here even if set to MAVEN_CENTRAL.
+_JGIT_REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL.
# set this to use a local version.
# "/home/<user>/projects/jgit"
@@ -26,28 +26,28 @@
name = "jgit_lib",
artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "7248b0a7d7f76dd4f7e55ed081b981cf4d8aa26e",
- src_sha1 = "6ed203c95decc3f795f44ca17149e7554b92212d",
+ sha1 = "69d8510b335d4d33d551a133505a4141311f970a",
+ src_sha1 = "6fd1eb331447b6163898b4d10aa769e2ac193740",
unsign = True,
)
maven_jar(
name = "jgit_servlet",
artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "f21fc0c651cc9475db92061432d919ba28b7a7ad",
+ sha1 = "93fb0075988b9c6bb97c725c03706f2341965b6b",
unsign = True,
)
maven_jar(
name = "jgit_archive",
artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "0f179321f527840dfc8ca79894eba2f6b255dbab",
+ sha1 = "a15aee805c758516ad7e9fa3f16e27bb9f4a1c2e",
)
maven_jar(
name = "jgit_junit",
artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "7e5225064cf14115bddaae9448246c83c89f78ad",
+ sha1 = "b6e712e743ea5798134f54547ae80456fad07f76",
unsign = True,
)
diff --git a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html
index c424808..62870df 100644
--- a/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-project-detail-list/gr-project-detail-list_test.html
@@ -69,6 +69,7 @@
element = fixture('basic');
element.detailType = 'branches';
counter = 0;
+ sandbox.stub(page, 'show');
});
teardown(() => {
@@ -314,6 +315,7 @@
element = fixture('basic');
element.detailType = 'tags';
counter = 0;
+ sandbox.stub(page, 'show');
});
teardown(() => {
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 f04b7c6..c8f606b 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
@@ -19,6 +19,7 @@
<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">
+<link rel="import" href="../gr-user-header/gr-user-header.html">
<dom-module id="gr-dashboard-view">
<template>
@@ -34,6 +35,9 @@
gr-change-list {
width: 100%;
}
+ .hide {
+ display: none;
+ }
@media only screen and (max-width: 50em) {
.loading {
padding: 0 var(--default-horizontal-margin);
@@ -42,6 +46,9 @@
</style>
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
<div hidden$="[[_loading]]" hidden>
+ <gr-user-header
+ user-id="[[params.user]]"
+ class$="[[_computeUserHeaderClass(params.user)]]"></gr-user-header>
<gr-change-list
show-star
show-reviewed-state
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index 750db92..0d56ac8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -53,6 +53,8 @@
},
/** @type {{ selectedChangeIndex: number }} */
viewState: Object,
+
+ /** @type {{ user: string }} */
params: {
type: Object,
},
@@ -144,5 +146,8 @@
return query.replace(/\$\{user\}/g, user);
},
+ _computeUserHeaderClass(userParam) {
+ return userParam === 'self' ? 'hide' : '';
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
index 32d1143..cdbb756 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
@@ -19,6 +19,7 @@
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../diff/gr-patch-range-select/gr-patch-range-select.html">
+<link rel="import" href="../../edit/gr-edit-controls/gr-edit-controls.html">
<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
@@ -74,6 +75,7 @@
display: none;
}
#diffPrefsContainer,
+ #editControlsContainer,
.rightControls {
align-self: flex-end;
margin: auto 0 auto auto;
@@ -112,7 +114,7 @@
.separator {
background-color: rgba(0, 0, 0, .3);
height: 1.5em;
- margin: 0 .4em;
+ margin: 0 .6em;
width: 1px;
}
.separator.transparent {
@@ -186,6 +188,9 @@
class="prefsButton desktop"
on-tap="_handlePrefsTap">Diff Preferences</gr-button>
</span>
+ <span id="editControlsContainer" class="showOnEdit">
+ <gr-edit-controls change="[[change]]"></gr-edit-controls>
+ </span>
</div>
</div>
<div class="fileList-header">
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
index 0fa6f4d..ab494b4 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
@@ -60,7 +60,7 @@
.separator {
background-color: rgba(0, 0, 0, .3);
height: 1.5em;
- margin: 0 .4em;
+ margin: 0 .6em;
width: 1px;
}
.separator.transparent {
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
index e95bd41..1bea61a 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
@@ -62,7 +62,7 @@
},
_getLinks(switchAccountUrl, path) {
- const links = [{name: 'Settings', url: '/settings'}];
+ const links = [{name: 'Settings', url: '/settings/'}];
if (switchAccountUrl) {
const replacements = {path};
const url = this._interpolateUrl(switchAccountUrl, replacements);
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 708cb17..474d0e6 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -87,8 +87,7 @@
PLUGIN_LIST_FILTER: '/admin/plugins/q/filter::filter',
PLUGIN_LIST_FILTER_OFFSET: '/admin/plugins/q/filter::filter,:offset',
- QUERY: '/q/:query',
- QUERY_OFFSET: '/q/:query,:offset',
+ QUERY: /^\/q\/([^,]+)(,(\d+))?$/,
// Matches /c/<changeNum>/[<basePatchNum>..][<patchNum>][/].
CHANGE_LEGACY: /^\/c\/(\d+)\/?(((-?\d+|edit)(\.\.(\d+|edit))?))?\/?$/,
@@ -227,10 +226,14 @@
} else if (params.view === Views.CHANGE) {
let range = this._getPatchRangeExpression(params);
if (range.length) { range = '/' + range; }
+ let suffix = `${range}`;
+ if (params.querystring) {
+ suffix += '?' + params.querystring;
+ }
if (params.project) {
- url = `/c/${params.project}/+/${params.changeNum}${range}`;
+ url = `/c/${params.project}/+/${params.changeNum}${suffix}`;
} else {
- url = `/c/${params.changeNum}${range}`;
+ url = `/c/${params.changeNum}${suffix}`;
}
} else if (params.view === Views.DASHBOARD) {
if (params.sections) {
@@ -542,7 +545,6 @@
this._mapRoute(RoutePattern.ADMIN_PLACEHOLDER,
'_handleAdminPlaceholderRoute', true);
- this._mapRoute(RoutePattern.QUERY_OFFSET, '_handleQueryRoute');
this._mapRoute(RoutePattern.QUERY, '_handleQueryRoute');
this._mapRoute(RoutePattern.CHANGE_NUMBER_LEGACY,
@@ -949,8 +951,11 @@
},
_handleQueryRoute(data) {
- data.params.view = Gerrit.Nav.View.SEARCH;
- this._setParams(data.params);
+ this._setParams({
+ view: Gerrit.Nav.View.SEARCH,
+ query: data.params[0],
+ offset: data.params[2],
+ });
},
_handleChangeNumberLegacyRoute(ctx) {
@@ -988,6 +993,7 @@
basePatchNum: ctx.params[3],
patchNum: ctx.params[5],
view: Gerrit.Nav.View.CHANGE,
+ querystring: ctx.querystring,
};
this._normalizeLegacyRouteParams(params);
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 4aae65f..b4fec5b 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
@@ -237,13 +237,28 @@
changeNum: '1234',
project: 'test',
};
+ const paramsWithQuery = {
+ view: Gerrit.Nav.View.CHANGE,
+ changeNum: '1234',
+ project: 'test',
+ querystring: 'revert&foo=bar',
+ };
+
assert.equal(element._generateUrl(params), '/c/test/+/1234');
+ assert.equal(element._generateUrl(paramsWithQuery),
+ '/c/test/+/1234?revert&foo=bar');
params.patchNum = 10;
assert.equal(element._generateUrl(params), '/c/test/+/1234/10');
+ paramsWithQuery.patchNum = 10;
+ assert.equal(element._generateUrl(paramsWithQuery),
+ '/c/test/+/1234/10?revert&foo=bar');
params.basePatchNum = 5;
assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10');
+ paramsWithQuery.basePatchNum = 5;
+ assert.equal(element._generateUrl(paramsWithQuery),
+ '/c/test/+/1234/5..10?revert&foo=bar');
});
test('diff', () => {
@@ -531,6 +546,22 @@
'/c/test/+/42#foo');
});
+ test('_handleQueryRoute', () => {
+ const data = {params: ['project:foo/bar/baz']};
+ assertDataToParams(data, '_handleQueryRoute', {
+ view: Gerrit.Nav.View.SEARCH,
+ query: 'project:foo/bar/baz',
+ offset: undefined,
+ });
+
+ data.params.push(',123', '123');
+ assertDataToParams(data, '_handleQueryRoute', {
+ view: Gerrit.Nav.View.SEARCH,
+ query: 'project:foo/bar/baz',
+ offset: '123',
+ });
+ });
+
suite('_handleRegisterRoute', () => {
test('happy path', () => {
const ctx = {params: ['/foo/bar']};
@@ -1103,6 +1134,7 @@
null, // 4 Unused
9, // 5 Patch number
],
+ querystring: '',
};
element._handleChangeLegacyRoute(ctx);
assert.isTrue(normalizeRouteStub.calledOnce);
@@ -1111,6 +1143,7 @@
basePatchNum: 6,
patchNum: 9,
view: Gerrit.Nav.View.CHANGE,
+ querystring: '',
});
});
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 3605e1d..f811e9e 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
@@ -51,6 +51,7 @@
'is:open',
'is:owner',
'is:pending',
+ 'is:private',
'is:reviewed',
'is:reviewer',
'is:starred',
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html
index 95a6167..f532e3f 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html
@@ -32,8 +32,17 @@
max-width: 15em;
}
.arrow {
+ color: rgba(0,0,0,.7);
margin: 0 .5em;
}
+ gr-dropdown-list {
+ --trigger-style: {
+ color: rgba(0,0,0,.7);
+ text-transform: none;
+ font-family: var(--font-family);
+ }
+ --trigger-hover-color: rgba(0,0,0,.6);
+ }
@media screen and (max-width: 50em) {
.filesWeblinks {
display: none;
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
index 5eef391..0b2985f 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
@@ -96,8 +96,9 @@
dropdownContent.push({
disabled: this._computeRightDisabled(patchNum, basePatchNum,
_sortedRevisions),
- triggerText: `Patchset ${patchNum}`,
- text: `Patchset ${patchNum}` +
+ triggerText: `${patchNum === 'edit' ? '': 'Patchset '}` +
+ patchNum,
+ text: `${patchNum === 'edit' ? '': 'Patchset '}${patchNum}` +
`${this._computePatchSetCommentsString(
this.comments, patchNum)}`,
mobileText: this._computeMobileText(patchNum, this.comments,
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
index 8ba3709..682956a 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
@@ -246,8 +246,8 @@
},
{
disabled: false,
- triggerText: 'Patchset edit',
- text: 'Patchset edit',
+ triggerText: 'edit',
+ text: 'edit',
mobileText: 'edit',
bottomText: '',
value: 'edit',
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
new file mode 100644
index 0000000..7ddf9a9
--- /dev/null
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
@@ -0,0 +1,86 @@
+<!--
+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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+
+<link rel="import" href="../../../bower_components/paper-input/paper-input.html">
+<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
+<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
+<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
+<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
+
+<link rel="import" href="../../../styles/shared-styles.html">
+
+<dom-module id="gr-edit-controls">
+ <template>
+ <style include="shared-styles">
+ :host {
+ align-items: center;
+ display: flex;
+ justify-content: flex-end;
+ }
+ .invisible {
+ display: none;
+ }
+ gr-button {
+ margin-left: 1em;
+ text-decoration: none;
+ }
+ paper-input {
+ --paper-input-container: {
+ padding: 0;
+ min-width: 15em;
+ }
+ --paper-input-container-input: {
+ font-size: 1em;
+ }
+ }
+ gr-confirm-dialog {
+ width: 50em;
+ }
+ gr-confirm-dialog .main {
+ width: 100%;
+ }
+ </style>
+ <template is="dom-repeat" items="[[_actions]]" as="action">
+ <gr-button
+ id$="[[action.key]]"
+ link
+ on-tap="_handleTap">[[action.label]]</gr-button>
+ </template>
+ <gr-overlay id="overlay" with-backdrop>
+ <gr-confirm-dialog
+ id="editDialog"
+ class="invisible"
+ disabled$="[[!_isValidPath(_path)]]"
+ confirm-label="Edit"
+ on-confirm="_handleEditConfirm"
+ on-cancel="_handleDialogCancel">
+ <div class="header">Edit a file</div>
+ <div class="main">
+ <!-- TODO(kaspern): Make this an autocomplete. -->
+ <paper-input
+ class="input"
+ label="Enter an existing or new full file path."
+ value="{{_path}}"></paper-input>
+ </div>
+ </gr-confirm-dialog>
+ </gr-overlay>
+ </template>
+ <script src="gr-edit-controls.js"></script>
+</dom-module>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
new file mode 100644
index 0000000..62e8c7a
--- /dev/null
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -0,0 +1,91 @@
+// 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.
+(function() {
+ 'use strict';
+
+ const Actions = {
+ EDIT: {label: 'Edit', key: 'edit'},
+ /* TODO(kaspern): Implement these actions.
+ DELETE: {label: 'Delete', key: 'delete'},
+ RENAME: {label: 'Rename', key: 'rename'},
+ REVERT: {label: 'Revert', key: 'revert'},
+ CHECKOUT: {label: 'Check out', key: 'checkout'},
+ */
+ };
+
+ Polymer({
+ is: 'gr-edit-controls',
+ properties: {
+ change: Object,
+
+ _actions: {
+ type: Array,
+ value() { return Object.values(Actions); },
+ },
+ _path: {
+ type: String,
+ value: '',
+ },
+ },
+
+ _handleTap(e) {
+ e.preventDefault();
+ const action = Polymer.dom(e).localTarget.id;
+ // TODO(kaspern): Add all actions to this switch.
+ switch (action) {
+ case Actions.EDIT.key:
+ this.openEditDialog();
+ return;
+ }
+ },
+
+ openEditDialog(opt_path) {
+ if (opt_path) { this._path = opt_path; }
+ return this._showDialog(this.$.editDialog);
+ },
+
+ /**
+ * Given a path string, checks that it is a valid file path.
+ * @param {string} path
+ * @return {boolean}
+ */
+ _isValidPath(path) {
+ return path.length && !path.endsWith('/');
+ },
+
+ _showDialog(dialog) {
+ return this.$.overlay.open().then(() => {
+ dialog.classList.toggle('invisible', false);
+ dialog.querySelector('.input').focus();
+ this.async(() => { this.$.overlay.center(); }, 1);
+ });
+ },
+
+ _closeDialog(dialog) {
+ dialog.querySelectorAll('.input').forEach(input => { input.value = ''; });
+ dialog.classList.toggle('invisible', true);
+ return this.$.overlay.close();
+ },
+
+ _handleDialogCancel(e) {
+ this._closeDialog(Polymer.dom(e).localTarget);
+ },
+
+ _handleEditConfirm(e) {
+ const url = Gerrit.Nav.getEditUrlForDiff(this.change, this._path);
+ Gerrit.Nav.navigateToRelativeUrl(url);
+ this._closeDialog(Polymer.dom(e).localTarget);
+ },
+ });
+})();
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
new file mode 100644
index 0000000..6da4e32
--- /dev/null
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
@@ -0,0 +1,100 @@
+<!--
+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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-edit-controls</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+
+<link rel="import" href="gr-edit-controls.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-edit-controls></gr-edit-controls>
+ </template>
+</test-fixture>
+
+<script>
+suite('gr-edit-controls tests', () => {
+ let element;
+ let sandbox;
+ let showDialogSpy;
+ let closeDialogSpy;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ showDialogSpy = sandbox.spy(element, '_showDialog');
+ closeDialogSpy = sandbox.spy(element, '_closeDialog');
+ flushAsynchronousOperations();
+ });
+
+ teardown(() => { sandbox.restore(); });
+
+ test('all actions exist', () => {
+ assert.equal(Polymer.dom(element.root).querySelectorAll('gr-button').length,
+ 1);
+ });
+
+ suite('edit button CUJ', () => {
+ let navStubs;
+
+ setup(() => {
+ navStubs = [
+ sandbox.stub(Gerrit.Nav, 'getEditUrlForDiff'),
+ sandbox.stub(Gerrit.Nav, 'navigateToRelativeUrl'),
+ ];
+ });
+
+ test('edit', () => {
+ MockInteractions.tap(element.$$('#edit'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.editDialog.disabled);
+ element._path = 'src/test.cpp';
+ assert.isFalse(element.$.editDialog.disabled);
+ MockInteractions.tap(element.$.editDialog.$$('gr-button[primary]'));
+ for (const stub of navStubs) { assert.isTrue(stub.called); }
+ assert.isTrue(closeDialogSpy.called);
+ });
+ });
+
+ test('cancel', () => {
+ MockInteractions.tap(element.$$('#edit'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.editDialog.disabled);
+ element._path = 'src/test.cpp';
+ assert.isFalse(element.$.editDialog.disabled);
+ MockInteractions.tap(element.$.editDialog.$$('gr-button'));
+ for (const stub of navStubs) { assert.isFalse(stub.called); }
+ assert.isTrue(closeDialogSpy.called);
+ assert.equal(element._path, '');
+ });
+ });
+ });
+
+ test('openEditDialog', () => {
+ return element.openEditDialog('test/path.cpp').then(() => {
+ assert.isFalse(element.$.editDialog.hasAttribute('hidden'));
+ assert.equal(element.$.editDialog.querySelector('.input').value,
+ 'test/path.cpp');
+ });
+ });
+});
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html
index dfaa6eb..c665df4 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html
@@ -44,7 +44,9 @@
<template is="dom-repeat" items="[[_agreements]]">
<tr>
<td class="nameColumn">
- <a href$="[[getUrlBase(item.url)]]">[[item.name]]</a>
+ <a href$="[[getUrlBase(item.url)]]" rel="external">
+ [[item.name]]
+ </a>
</td>
<td class="descriptionColumn">[[item.description]]</td>
</tr>
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
index c1c0338..83fb7cb 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
@@ -25,11 +25,11 @@
<dom-module id="gr-autocomplete-dropdown">
<template>
<style include="shared-styles">
- /* This must be set here vs. the container component because in some cases
- the element is moved in the DOM to a base element and is no longer a
- child of its original parent. */
- :host(.fixed){
- position: fixed;
+ :host {
+ z-index: 100;
+ }
+ :host([is-hidden]) {
+ display: none;
}
ul {
list-style: none;
@@ -49,29 +49,22 @@
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
}
</style>
- <iron-dropdown
- id="dropdown"
- allow-outside-scroll="true"
- vertical-align="top"
- horizontal-align="auto"
- vertical-offset="[[verticalOffset]]">
- <div
- class="dropdown-content"
- slot="dropdown-content"
- id="suggestions"
- role="listbox">
- <ul>
- <template is="dom-repeat" items="[[suggestions]]">
- <li data-index$="[[index]]"
- data-value$="[[item.dataValue]]"
- tabindex="-1"
- aria-label$="[[item.name]]"
- role="option"
- on-tap="_handleTapItem">[[item.text]]</li>
- </template>
- </ul>
- </div>
- </iron-dropdown>
+ <div
+ class="dropdown-content"
+ slot="dropdown-content"
+ id="suggestions"
+ role="listbox">
+ <ul>
+ <template is="dom-repeat" items="[[suggestions]]">
+ <li data-index$="[[index]]"
+ data-value$="[[item.dataValue]]"
+ tabindex="-1"
+ aria-label$="[[item.name]]"
+ role="option"
+ on-tap="_handleTapItem">[[item.text]]</li>
+ </template>
+ </ul>
+ </div>
<gr-cursor-manager
id="cursor"
index="{{index}}"
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
index 100b5ca..c0449be 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
@@ -14,9 +14,6 @@
(function() {
'use strict';
- const AWAIT_MAX_ITERS = 10;
- const AWAIT_STEP = 5;
-
Polymer({
is: 'gr-autocomplete-dropdown',
@@ -34,6 +31,11 @@
properties: {
index: Number,
+ isHidden: {
+ type: Boolean,
+ value: true,
+ reflectToAttribute: true,
+ },
verticalOffset: {
type: Number,
value: null,
@@ -53,6 +55,7 @@
},
behaviors: [
+ Polymer.IronFitBehavior,
Gerrit.KeyboardShortcutBehavior,
],
@@ -65,46 +68,14 @@
},
close() {
- this.$.dropdown.close();
+ this.isHidden = true;
},
open() {
- this._open().then(() => {
- this._resetCursorStops();
- this._resetCursorIndex();
- this.fire('open-complete');
- });
- },
-
- // TODO (beckysiegel) look into making this a behavior since it's used
- // 3 times now.
- _open(...args) {
- return new Promise(resolve => {
- Polymer.IronOverlayBehaviorImpl.open.apply(this.$.dropdown, args);
- this._awaitOpen(resolve);
- });
- },
-
- /**
- * NOTE: (wyatta) Slightly hacky way to listen to the overlay actually
- * opening. Eventually replace with a direct way to listen to the overlay.
- */
- _awaitOpen(fn) {
- let iters = 0;
- const step = () => {
- this.async(() => {
- if (this.style.display !== 'none') {
- fn.call(this);
- } else if (iters++ < AWAIT_MAX_ITERS) {
- step.call(this);
- }
- }, AWAIT_STEP);
- };
- step.call(this);
- },
-
- get isHidden() {
- return !this.$.dropdown.opened;
+ this.isHidden = false;
+ this.refit();
+ this._resetCursorStops();
+ this._resetCursorIndex();
},
getCurrentText() {
@@ -112,7 +83,7 @@
},
_handleUp(e) {
- if (!this.hidden) {
+ if (!this.isHidden) {
e.preventDefault();
e.stopPropagation();
this.cursorUp();
@@ -120,7 +91,7 @@
},
_handleDown(e) {
- if (!this.hidden) {
+ if (!this.isHidden) {
e.preventDefault();
e.stopPropagation();
this.cursorDown();
@@ -128,13 +99,13 @@
},
cursorDown() {
- if (!this.hidden) {
+ if (!this.isHidden) {
this.$.cursor.next();
}
},
cursorUp() {
- if (!this.hidden) {
+ if (!this.isHidden) {
this.$.cursor.previous();
}
},
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
index 23b27be..1995688 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
@@ -52,7 +52,7 @@
});
test('escape key', done => {
- const closeSpy = sandbox.spy(element.$.dropdown, 'close');
+ const closeSpy = sandbox.spy(element, 'close');
MockInteractions.pressAndReleaseKeyOn(element, 27);
flushAsynchronousOperations();
assert.isTrue(closeSpy.called);
@@ -87,24 +87,24 @@
});
test('down key', () => {
- element.hidden = true;
+ element.isHidden = true;
const nextSpy = sandbox.spy(element.$.cursor, 'next');
MockInteractions.pressAndReleaseKeyOn(element, 40);
assert.isFalse(nextSpy.called);
assert.equal(element.$.cursor.index, 0);
- element.hidden = false;
+ element.isHidden = false;
MockInteractions.pressAndReleaseKeyOn(element, 40);
assert.isTrue(nextSpy.called);
assert.equal(element.$.cursor.index, 1);
});
test('up key', () => {
- element.hidden = true;
+ element.isHidden = true;
const prevSpy = sandbox.spy(element.$.cursor, 'previous');
MockInteractions.pressAndReleaseKeyOn(element, 38);
assert.isFalse(prevSpy.called);
assert.equal(element.$.cursor.index, 0);
- element.hidden = false;
+ element.isHidden = false;
element.$.cursor.setCursorAtIndex(1);
assert.equal(element.$.cursor.index, 1);
MockInteractions.pressAndReleaseKeyOn(element, 38);
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
index 81ac90e..7aa7abf 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -38,26 +38,29 @@
color: red;
}
</style>
- <input
- id="input"
- class$="[[_computeClass(borderless)]]"
- is="iron-input"
- disabled$="[[disabled]]"
- bind-value="{{text}}"
- placeholder="[[placeholder]]"
- on-keydown="_handleKeydown"
- on-focus="_onInputFocus"
- on-blur="_onInputBlur"
- autocomplete="off"/>
- <!-- This container is needed for Safari and Firefox -->
- <div id="suggestionContainer">
+ <div>
+ <input
+ id="input"
+ class$="[[_computeClass(borderless)]]"
+ is="iron-input"
+ disabled$="[[disabled]]"
+ bind-value="{{text}}"
+ placeholder="[[placeholder]]"
+ on-keydown="_handleKeydown"
+ on-focus="_onInputFocus"
+ on-blur="_onInputBlur"
+ autocomplete="off"/>
<gr-autocomplete-dropdown
+ vertical-align="top"
+ vertical-offset="20"
+ horizontal-align="auto"
id="suggestions"
on-item-selected="_handleItemSelect"
on-keydown="_handleKeydown"
suggestions="[[_suggestions]]"
role="listbox"
- index="[[_index]]">
+ index="[[_index]]"
+ position-target="[[_inputElement]]">
</gr-autocomplete-dropdown>
</div>
</template>
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 aa20f96..18c811d 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -185,6 +185,10 @@
this._commit();
},
+ get _inputElement() {
+ return this.$.input;
+ },
+
/**
* Set the text of the input without triggering the suggestion dropdown.
* @param {string} text The new text for the input.
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html
index 91c2def..0916a89 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html
@@ -72,10 +72,6 @@
background-color: #f2f2f2;
}
}
- gr-button {
- --gr-button-arrow-color: var(--color-link);
- --gr-button-arrow-hover-color: var(--color-link-hover);
- }
paper-item:not(:last-of-type) {
border-bottom: 1px solid #ddd;
}
@@ -95,6 +91,12 @@
flex-direction: row;
width: 100%;
}
+ gr-button {
+ --gr-button: {
+ @apply --trigger-style;
+ }
+ --gr-button-hover-color: var(--trigger-hover-color);
+ }
gr-date-formatter {
color: rgba(0,0,0,.54);
margin-left: 2em;
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
index 95df7c9..3c28674 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
@@ -67,12 +67,19 @@
}
</style>
<div id="hiddenText"></div>
+ <!-- When the autocomplete is open, the span is moved at the end of
+ hiddenText in order to correctly position the dropdown. After being moved,
+ it is set as the positionTarget for the emojiSuggestions dropdown. -->
+ <span id="caratSpan"></span>
<gr-autocomplete-dropdown
+ vertical-align="top"
+ horizontal-align="left"
+ dynamic-align
id="emojiSuggestions"
suggestions="[[_suggestions]]"
index="[[_index]]"
vertical-offset="[[_verticalOffset]]"
- on-dropdown-closed="_resetAndFocus"
+ on-dropdown-closed="_resetEmojiDropdown"
on-item-selected="_handleEmojiSelect">
</gr-autocomplete-dropdown>
<iron-autogrow-textarea
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
index 2d796bd..519db55 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -125,7 +125,6 @@
}
},
-
closeDropdown() {
return this.$.emojiSuggestions.close();
},
@@ -148,10 +147,6 @@
if (this._hideAutocomplete) { return; }
e.preventDefault();
e.stopPropagation();
- this._resetAndFocus();
- },
-
- _resetAndFocus() {
this._resetEmojiDropdown();
},
@@ -190,15 +185,19 @@
},
/**
* Uses a hidden element with the same width and styling of the textarea and
- * the text up until the point of interest. Then the emoji selection
- * element is added to the end so that they are correctly positioned by the
- * end of the last character entered.
+ * the text up until the point of interest. Then caratSpan element is added
+ * to the end and is set to be the positionTarget for the dropdown. Together
+ * this allows the dropdown to appear near where the user is typing.
*/
_updateCaratPosition() {
+ this._hideAutocomplete = false;
this.$.hiddenText.textContent = this.$.textarea.value.substr(0,
this.$.textarea.selectionStart);
- this.$.hiddenText.appendChild(this.$.emojiSuggestions);
+ const caratSpan = this.$.caratSpan;
+ this.$.hiddenText.appendChild(caratSpan);
+ this.$.emojiSuggestions.positionTarget = caratSpan;
+ this._openEmojiDropdown();
},
_getFontSize() {
@@ -250,8 +249,6 @@
// Otherwise open the dropdown and set the position to be just below the
// cursor.
} else if (this.$.emojiSuggestions.isHidden) {
- this._hideAutocomplete = false;
- this._openEmojiDropdown();
this._updateCaratPosition();
}
this.$.textarea.textarea.focus();
@@ -268,7 +265,7 @@
suggestion.text = suggestion.value + ' ' + suggestion.match;
suggestions.push(suggestion);
}
- this._suggestions = suggestions;
+ this.set('_suggestions', suggestions);
},
_determineSuggestions(emojiText) {
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
index 493dd5d..0434865 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
@@ -171,11 +171,11 @@
element.text = 'test';
element._updateCaratPosition();
assert.deepEqual(element.$.hiddenText.innerHTML, element.text +
- element.$.emojiSuggestions.outerHTML);
+ element.$.caratSpan.outerHTML);
});
test('emoji dropdown is closed when iron-overlay-closed is fired', () => {
- const resetSpy = sandbox.spy(element, '_resetAndFocus');
+ const resetSpy = sandbox.spy(element, '_resetEmojiDropdown');
element.$.emojiSuggestions.fire('dropdown-closed');
assert.isTrue(resetSpy.called);
});
@@ -190,10 +190,6 @@
suite('keyboard shortcuts', () => {
function setupDropdown(callback) {
- element.$.emojiSuggestions.addEventListener('open-complete', () => {
- callback();
- });
- flushAsynchronousOperations();
MockInteractions.focus(element.$.textarea);
element.$.textarea.selectionStart = 1;
element.$.textarea.selectionEnd = 1;
@@ -201,55 +197,48 @@
element.$.textarea.selectionStart = 1;
element.$.textarea.selectionEnd = 2;
element.text = ':1';
+ flushAsynchronousOperations();
}
- test('escape key', done => {
- const resestSpy = sandbox.spy(element, '_resetAndFocus');
+ test('escape key', () => {
+ const resetSpy = sandbox.spy(element, '_resetEmojiDropdown');
MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 27);
- assert.isFalse(resestSpy.called);
- setupDropdown(() => {
- MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 27);
- assert.isTrue(resestSpy.called);
- assert.isFalse(!element.$.emojiSuggestions.isHidden);
- done();
- });
+ assert.isFalse(resetSpy.called);
+ setupDropdown();
+ MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 27);
+ assert.isTrue(resetSpy.called);
+ assert.isFalse(!element.$.emojiSuggestions.isHidden);
});
- test('up key', done => {
+ test('up key', () => {
const upSpy = sandbox.spy(element.$.emojiSuggestions, 'cursorUp');
MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 38);
assert.isFalse(upSpy.called);
- setupDropdown(() => {
- MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 38);
- assert.isTrue(upSpy.called);
- done();
- });
+ setupDropdown();
+ MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 38);
+ assert.isTrue(upSpy.called);
});
- test('down key', done => {
+ test('down key', () => {
const downSpy = sandbox.spy(element.$.emojiSuggestions, 'cursorDown');
MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 40);
assert.isFalse(downSpy.called);
- setupDropdown(() => {
- MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 40);
- assert.isTrue(downSpy.called);
- done();
- });
+ setupDropdown();
+ MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 40);
+ assert.isTrue(downSpy.called);
});
- test('enter key', done => {
+ test('enter key', () => {
const enterSpy = sandbox.spy(element.$.emojiSuggestions,
'getCursorTarget');
MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
assert.isFalse(enterSpy.called);
- setupDropdown(() => {
- MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
- assert.isTrue(enterSpy.called);
- flushAsynchronousOperations();
- // A space is automatically added at the end.
- assert.equal(element.text, '💯 ');
- done();
- });
+ setupDropdown();
+ MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
+ assert.isTrue(enterSpy.called);
+ flushAsynchronousOperations();
+ // A space is automatically added at the end.
+ assert.equal(element.text, '💯 ');
});
});
});
diff --git a/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py b/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
index b128d65..2f6b030 100644
--- a/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
+++ b/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
@@ -89,6 +89,7 @@
# Special case for polymer behaviors we are using.
replaceBehaviorLikeHTML("polygerrit-ui/app/bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html", "iron-a11y-keys-behavior.html")
generateStubBehavior("Polymer.IronOverlayBehavior")
+ generateStubBehavior("Polymer.IronFitBehavior")
#TODO figure out something to do with iron-overlay-behavior. it is hard-coded reformatted.
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index b0310c6..7cc37d8 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -103,6 +103,7 @@
'diff/gr-selection-action-box/gr-selection-action-box_test.html',
'diff/gr-syntax-layer/gr-syntax-layer_test.html',
'diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html',
+ 'edit/gr-edit-controls/gr-edit-controls_test.html',
'edit/gr-edit-file-controls/gr-edit-file-controls_test.html',
'edit/gr-editor-view/gr-editor-view_test.html',
'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
diff --git a/version.bzl b/version.bzl
index 62d841f..9a97721 100644
--- a/version.bzl
+++ b/version.bzl
@@ -3,3 +3,7 @@
# when talking to the destination repository.
#
GERRIT_VERSION = "2.16-SNAPSHOT"
+
+def check_version(x):
+ if native.bazel_version < x:
+ fail("\nERROR: Current Bazel version is {}, expected at least {}\n".format(native.bazel_version, x))