Merge "Merge branch 'stable-2.15' into stable-2.16" into stable-2.16
diff --git a/Documentation/config-auto-site-initialization.txt b/Documentation/config-auto-site-initialization.txt
index acd03c9..1be0af9 100644
--- a/Documentation/config-auto-site-initialization.txt
+++ b/Documentation/config-auto-site-initialization.txt
@@ -27,15 +27,14 @@
run for that site. The database connectivity, in that case, is defined
in the `etc/gerrit.config`.
-If `gerrit.site_path` is not defined then Gerrit will try to find the
-`gerrit.init_path` system property. If defined this property will be
-used to determine the site path. The database connectivity, also for
-this case, is defined by the `jdbc/ReviewDb` JNDI property.
+`gerrit.site_path` system property must be defined to run the init for
+that site.
[WARNING]
Defining the `jdbc/ReviewDb` JNDI property for an H2 database under the
-path defined by either `gerrit.site_path` or `gerrit.init_path` will
-cause an incomplete auto initialization and Gerrit will fail to start.
+path defined by `gerrit.site_path` will cause an incomplete auto
+initialization and Gerrit will fail to start.
+
Opening a connection to such a database will create a subfolder under the
site path folder (in order to create the H2 database) and Gerrit will
no longer consider that site path to be new and, because of that,
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index be50d3b..3927b49 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -5090,16 +5090,6 @@
The format is one Base-64 encoded public key per line.
-== Database system_config
-
-Several columns in the `system_config` table within the metadata
-database may be set to control how Gerrit behaves.
-
-[NOTE]
-The contents of the `system_config` table are cached at startup
-by Gerrit. If you modify any columns in this table, Gerrit needs
-to be restarted before it will use the new values.
-
== Configuring the Polygerrit UI
Please see link:dev-polygerrit.html[UI] on configuring the Polygerrit UI.
diff --git a/Documentation/install-j2ee.txt b/Documentation/install-j2ee.txt
index f7252e0..91d73cc 100644
--- a/Documentation/install-j2ee.txt
+++ b/Documentation/install-j2ee.txt
@@ -105,9 +105,8 @@
----
[TIP]
-Under Jetty, restarting the web application (e.g. after modifying
-`system_config`) is as simple as touching the context config file:
-`'$JETTY_HOME'/contexts/gerrit.xml`
+Under Jetty, restarting the web application is as simple as
+touching the context config file: `'$JETTY_HOME'/contexts/gerrit.xml`
[[tomcat]]
== Tomcat 7.x
diff --git a/WORKSPACE b/WORKSPACE
index b2b3b6f..322d93f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -728,10 +728,10 @@
maven_jar(
name = "blame-cache",
- artifact = "com/google/gitiles:blame-cache:0.2-6",
+ artifact = "com/google/gitiles:blame-cache:0.2-7",
attach_source = False,
repository = GERRIT,
- sha1 = "64827f1bc2cbdbb6515f1d29ce115db94c03bb6a",
+ sha1 = "8170f33b8b1db6f55e41d7069fa050a4d102a62b",
)
# Keep this version of Soy synchronized with the version used in Gitiles.
diff --git a/java/com/google/gerrit/httpd/init/SiteInitializer.java b/java/com/google/gerrit/httpd/init/SiteInitializer.java
index de4f284..67510cd 100644
--- a/java/com/google/gerrit/httpd/init/SiteInitializer.java
+++ b/java/com/google/gerrit/httpd/init/SiteInitializer.java
@@ -14,19 +14,17 @@
package com.google.gerrit.httpd.init;
+import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.pgm.init.BaseInit;
import com.google.gerrit.pgm.init.PluginsDistribution;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
import java.util.List;
public final class SiteInitializer {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final String GERRIT_SITE_PATH = "gerrit.site_path";
private final String sitePath;
private final String initPath;
@@ -53,42 +51,29 @@
return;
}
- try (Connection conn = connectToDb()) {
- Path site = getSiteFromReviewDb(conn);
- if (site == null && initPath != null) {
- site = Paths.get(initPath);
- }
- if (site != null) {
- logger.atInfo().log("Initializing site at %s", site.toRealPath().normalize());
- new BaseInit(
- site,
- new ReviewDbDataSourceProvider(),
- false,
- false,
- pluginsDistribution,
- pluginsToInstall)
- .run();
- }
+ String path = System.getProperty(GERRIT_SITE_PATH);
+ Path site = null;
+ if (!Strings.isNullOrEmpty(path)) {
+ site = Paths.get(path);
+ }
+
+ if (site == null && initPath != null) {
+ site = Paths.get(initPath);
+ }
+ if (site != null) {
+ logger.atInfo().log("Initializing site at %s", site.toRealPath().normalize());
+ new BaseInit(
+ site,
+ new ReviewDbDataSourceProvider(),
+ false,
+ false,
+ pluginsDistribution,
+ pluginsToInstall)
+ .run();
}
} catch (Exception e) {
logger.atSevere().withCause(e).log("Site init failed");
throw new RuntimeException(e);
}
}
-
- private Connection connectToDb() throws SQLException {
- return new ReviewDbDataSourceProvider().get().getConnection();
- }
-
- private Path getSiteFromReviewDb(Connection conn) {
- try (Statement stmt = conn.createStatement();
- ResultSet rs = stmt.executeQuery("SELECT site_path FROM system_config")) {
- if (rs.next()) {
- return Paths.get(rs.getString(1));
- }
- } catch (SQLException e) {
- return null;
- }
- return null;
- }
}
diff --git a/java/com/google/gerrit/httpd/init/SitePathFromSystemConfigProvider.java b/java/com/google/gerrit/httpd/init/SitePathFromSystemConfigProvider.java
deleted file mode 100644
index 96ba28b..0000000
--- a/java/com/google/gerrit/httpd/init/SitePathFromSystemConfigProvider.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2009 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.httpd.init;
-
-import com.google.gerrit.reviewdb.client.SystemConfig;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.SitePath;
-import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-
-/** Provides {@link Path} annotated with {@link SitePath}. */
-class SitePathFromSystemConfigProvider implements Provider<Path> {
- private final Path path;
-
- @Inject
- SitePathFromSystemConfigProvider(@ReviewDbFactory SchemaFactory<ReviewDb> schemaFactory)
- throws OrmException {
- path = read(schemaFactory);
- }
-
- @Override
- public Path get() {
- return path;
- }
-
- private static Path read(SchemaFactory<ReviewDb> schemaFactory) throws OrmException {
- try (ReviewDb db = schemaFactory.open()) {
- List<SystemConfig> all = db.systemConfig().all().toList();
- switch (all.size()) {
- case 1:
- return Paths.get(all.get(0).sitePath);
- case 0:
- throw new OrmException("system_config table is empty");
- default:
- throw new OrmException(
- "system_config must have exactly 1 row; found " + all.size() + " rows instead");
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index ec13514..75858de 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -107,6 +107,7 @@
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
import com.google.inject.name.Names;
import com.google.inject.servlet.GuiceFilter;
import com.google.inject.servlet.GuiceServletContextListener;
@@ -134,6 +135,8 @@
public class WebAppInitializer extends GuiceServletContextListener implements Filter {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final String GERRIT_SITE_PATH = "gerrit.site_path";
+
private Path sitePath;
private Injector dbInjector;
private Injector cfgInjector;
@@ -155,9 +158,11 @@
private synchronized void init() {
if (manager == null) {
- final String path = System.getProperty("gerrit.site_path");
+ String path = System.getProperty(GERRIT_SITE_PATH);
if (path != null) {
sitePath = Paths.get(path);
+ } else {
+ throw new ProvisionException(GERRIT_SITE_PATH + " must be defined");
}
if (System.getProperty("gerrit.init") != null) {
@@ -171,7 +176,7 @@
}
new SiteInitializer(
path,
- System.getProperty("gerrit.init_path"),
+ System.getProperty(GERRIT_SITE_PATH),
new UnzippedDistribution(servletContext),
pluginsToInstall)
.init();
@@ -292,21 +297,6 @@
listener().to(ReviewDbDataSourceProvider.class);
}
});
-
- // If we didn't get the site path from the system property
- // we need to get it from the database, as that's our old
- // method of locating the site path on disk.
- //
- modules.add(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(Path.class)
- .annotatedWith(SitePath.class)
- .toProvider(SitePathFromSystemConfigProvider.class)
- .in(SINGLETON);
- }
- });
modules.add(new GerritServerConfigModule());
}
modules.add(new DatabaseModule());
diff --git a/java/com/google/gerrit/reviewdb/client/SystemConfig.java b/java/com/google/gerrit/reviewdb/client/SystemConfig.java
deleted file mode 100644
index cd42dd1..0000000
--- a/java/com/google/gerrit/reviewdb/client/SystemConfig.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2008 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.reviewdb.client;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-/** Global configuration needed to serve web requests. */
-public final class SystemConfig {
- public static final class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- private static final String VALUE = "X";
-
- @Column(id = 1, length = 1)
- protected String one = VALUE;
-
- public Key() {}
-
- @Override
- public String get() {
- return VALUE;
- }
-
- @Override
- protected void set(String newValue) {
- assert get().equals(newValue);
- }
- }
-
- /** Construct a new, unconfigured instance. */
- public static SystemConfig create() {
- final SystemConfig r = new SystemConfig();
- r.singleton = new SystemConfig.Key();
- return r;
- }
-
- @Column(id = 1)
- protected Key singleton;
-
- /** Local filesystem location of header/footer/CSS configuration files */
- @Column(id = 3, notNull = false, length = Integer.MAX_VALUE)
- public transient String sitePath;
-
- // DO NOT LOOK BELOW THIS LINE. These fields have all been deleted,
- // but survive to support schema upgrade code.
-
- /** DEPRECATED DO NOT USE */
- @Column(id = 2, length = 36, notNull = false)
- public transient String registerEmailPrivateKey;
- /** DEPRECATED DO NOT USE */
- @Column(id = 4, notNull = false)
- public AccountGroup.Id adminGroupId;
- /** DEPRECATED DO NOT USE */
- @Column(id = 10, notNull = false)
- public AccountGroup.UUID adminGroupUUID;
- /** DEPRECATED DO NOT USE */
- @Column(id = 5, notNull = false)
- public AccountGroup.Id anonymousGroupId;
- /** DEPRECATED DO NOT USE */
- @Column(id = 6, notNull = false)
- public AccountGroup.Id registeredGroupId;
- /** DEPRECATED DO NOT USE */
- @Column(id = 7, notNull = false)
- public Project.NameKey wildProjectName;
- /** DEPRECATED DO NOT USE */
- @Column(id = 9, notNull = false)
- public AccountGroup.Id ownerGroupId;
- /** DEPRECATED DO NOT USE */
- @Column(id = 8, notNull = false)
- public AccountGroup.Id batchUsersGroupId;
- /** DEPRECATED DO NOT USE */
- @Column(id = 11, notNull = false)
- public AccountGroup.UUID batchUsersGroupUUID;
-
- protected SystemConfig() {}
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDb.java b/java/com/google/gerrit/reviewdb/server/ReviewDb.java
index 4e648b9..f0661e9 100644
--- a/java/com/google/gerrit/reviewdb/server/ReviewDb.java
+++ b/java/com/google/gerrit/reviewdb/server/ReviewDb.java
@@ -17,7 +17,6 @@
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.SystemConfig;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.Relation;
import com.google.gwtorm.server.Schema;
@@ -31,7 +30,6 @@
* <ul>
* <li>{@link Account}: Per-user account registration, preferences, identity.
* <li>{@link Change}: All review information about a single proposed change.
- * <li>{@link SystemConfig}: Server-wide settings, managed by administrator.
* </ul>
*/
public interface ReviewDb extends Schema {
@@ -40,8 +38,7 @@
@Relation(id = 1)
SchemaVersionAccess schemaVersion();
- @Relation(id = 2)
- SystemConfigAccess systemConfig();
+ // Deleted @Relation(id = 2)
// Deleted @Relation(id = 3)
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java b/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
index 0deaa57..202729e 100644
--- a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
+++ b/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
@@ -110,11 +110,6 @@
}
@Override
- public SystemConfigAccess systemConfig() {
- return delegate.systemConfig();
- }
-
- @Override
public ChangeAccess changes() {
return delegate.changes();
}
diff --git a/java/com/google/gerrit/reviewdb/server/SystemConfigAccess.java b/java/com/google/gerrit/reviewdb/server/SystemConfigAccess.java
deleted file mode 100644
index a2177fd..0000000
--- a/java/com/google/gerrit/reviewdb/server/SystemConfigAccess.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2008 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.reviewdb.server;
-
-import com.google.gerrit.reviewdb.client.SystemConfig;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-/** Access interface for {@link SystemConfig}. */
-public interface SystemConfigAccess extends Access<SystemConfig, SystemConfig.Key> {
- @Override
- @PrimaryKey("singleton")
- SystemConfig get(SystemConfig.Key key) throws OrmException;
-
- @Query
- ResultSet<SystemConfig> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java b/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java
index 66d6555..0bfe5fd 100644
--- a/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java
+++ b/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java
@@ -13,10 +13,11 @@
// limitations under the License.
package com.google.gerrit.server.config;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
import java.util.Collections;
import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
@@ -36,6 +37,8 @@
* (+ various overloaded versions of these)
*/
public class ConfigUpdatedEvent {
+ public static final Multimap<UpdateResult, ConfigUpdateEntry> NO_UPDATES =
+ new ImmutableMultimap.Builder<UpdateResult, ConfigUpdateEntry>().build();
private final Config oldConfig;
private final Config newConfig;
@@ -52,25 +55,29 @@
return this.newConfig;
}
- public Update accept(ConfigKey entry) {
+ private String getString(ConfigKey key, Config config) {
+ return config.getString(key.section(), key.subsection(), key.name());
+ }
+
+ public Multimap<UpdateResult, ConfigUpdateEntry> accept(ConfigKey entry) {
return accept(Collections.singleton(entry));
}
- public Update accept(Set<ConfigKey> entries) {
+ public Multimap<UpdateResult, ConfigUpdateEntry> accept(Set<ConfigKey> entries) {
return createUpdate(entries, UpdateResult.APPLIED);
}
- public Update accept(String section) {
+ public Multimap<UpdateResult, ConfigUpdateEntry> accept(String section) {
Set<ConfigKey> entries = getEntriesFromSection(oldConfig, section);
entries.addAll(getEntriesFromSection(newConfig, section));
return createUpdate(entries, UpdateResult.APPLIED);
}
- public Update reject(ConfigKey entry) {
+ public Multimap<UpdateResult, ConfigUpdateEntry> reject(ConfigKey entry) {
return reject(Collections.singleton(entry));
}
- public Update reject(Set<ConfigKey> entries) {
+ public Multimap<UpdateResult, ConfigUpdateEntry> reject(Set<ConfigKey> entries) {
return createUpdate(entries, UpdateResult.REJECTED);
}
@@ -87,20 +94,15 @@
return res;
}
- private Update createUpdate(Set<ConfigKey> entries, UpdateResult updateResult) {
- Update update = new Update(updateResult);
+ private Multimap<UpdateResult, ConfigUpdateEntry> createUpdate(
+ Set<ConfigKey> entries, UpdateResult updateResult) {
+ Multimap<UpdateResult, ConfigUpdateEntry> updates = ArrayListMultimap.create();
entries
.stream()
.filter(this::isValueUpdated)
- .forEach(
- key -> {
- update.addConfigUpdate(
- new ConfigUpdateEntry(
- key,
- oldConfig.getString(key.section(), key.subsection(), key.name()),
- newConfig.getString(key.section(), key.subsection(), key.name())));
- });
- return update;
+ .map(e -> new ConfigUpdateEntry(e, getString(e, oldConfig), getString(e, newConfig)))
+ .forEach(e -> updates.put(updateResult, e));
+ return updates;
}
public boolean isSectionUpdated(String section) {
@@ -142,31 +144,6 @@
}
}
- /**
- * One Accepted/Rejected Update have one or more config updates (ConfigUpdateEntry) tied to it.
- */
- public static class Update {
- private UpdateResult result;
- private final Set<ConfigUpdateEntry> configUpdates;
-
- public Update(UpdateResult result) {
- this.configUpdates = new LinkedHashSet<>();
- this.result = result;
- }
-
- public UpdateResult getResult() {
- return result;
- }
-
- public List<ConfigUpdateEntry> getConfigUpdates() {
- return ImmutableList.copyOf(configUpdates);
- }
-
- public void addConfigUpdate(ConfigUpdateEntry entry) {
- this.configUpdates.add(entry);
- }
- }
-
public enum ConfigEntryType {
ADDED,
REMOVED,
diff --git a/java/com/google/gerrit/server/config/GerritConfigListener.java b/java/com/google/gerrit/server/config/GerritConfigListener.java
index 337a962..f5b2976 100644
--- a/java/com/google/gerrit/server/config/GerritConfigListener.java
+++ b/java/com/google/gerrit/server/config/GerritConfigListener.java
@@ -14,9 +14,11 @@
package com.google.gerrit.server.config;
+import com.google.common.collect.Multimap;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
import java.util.EventListener;
-import java.util.List;
/**
* Implementations of the GerritConfigListener interface expects to react GerritServerConfig
@@ -24,5 +26,5 @@
*/
@ExtensionPoint
public interface GerritConfigListener extends EventListener {
- List<ConfigUpdatedEvent.Update> configUpdated(ConfigUpdatedEvent event);
+ Multimap<UpdateResult, ConfigUpdateEntry> configUpdated(ConfigUpdatedEvent event);
}
diff --git a/java/com/google/gerrit/server/config/GerritConfigListenerHelper.java b/java/com/google/gerrit/server/config/GerritConfigListenerHelper.java
index 1dfa3fc..d21e1c3 100644
--- a/java/com/google/gerrit/server/config/GerritConfigListenerHelper.java
+++ b/java/com/google/gerrit/server/config/GerritConfigListenerHelper.java
@@ -15,13 +15,12 @@
package com.google.gerrit.server.config;
import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
public class GerritConfigListenerHelper {
public static GerritConfigListener acceptIfChanged(ConfigKey... keys) {
return e ->
e.isEntriesUpdated(ImmutableSet.copyOf(keys))
- ? Collections.singletonList(e.accept(ImmutableSet.copyOf(keys)))
- : Collections.emptyList();
+ ? e.accept(ImmutableSet.copyOf(keys))
+ : ConfigUpdatedEvent.NO_UPDATES;
}
}
diff --git a/java/com/google/gerrit/server/config/GerritServerConfigReloader.java b/java/com/google/gerrit/server/config/GerritServerConfigReloader.java
index 1890de8..09c10740 100644
--- a/java/com/google/gerrit/server/config/GerritServerConfigReloader.java
+++ b/java/com/google/gerrit/server/config/GerritServerConfigReloader.java
@@ -14,12 +14,14 @@
package com.google.gerrit.server.config;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import java.util.ArrayList;
-import java.util.List;
/** Issues a configuration reload from the GerritServerConfigProvider and notify all listeners. */
@Singleton
@@ -40,18 +42,20 @@
* Reloads the Gerrit Server Configuration from disk. Synchronized to ensure that one issued
* reload is fully completed before a new one starts.
*/
- public List<ConfigUpdatedEvent.Update> reloadConfig() {
+ public Multimap<UpdateResult, ConfigUpdateEntry> reloadConfig() {
logger.atInfo().log("Starting server configuration reload");
- List<ConfigUpdatedEvent.Update> updates = fireUpdatedConfigEvent(configProvider.updateConfig());
+ Multimap<UpdateResult, ConfigUpdateEntry> updates =
+ fireUpdatedConfigEvent(configProvider.updateConfig());
logger.atInfo().log("Server configuration reload completed succesfully");
return updates;
}
- public List<ConfigUpdatedEvent.Update> fireUpdatedConfigEvent(ConfigUpdatedEvent event) {
- ArrayList<ConfigUpdatedEvent.Update> result = new ArrayList<>();
+ public Multimap<UpdateResult, ConfigUpdateEntry> fireUpdatedConfigEvent(
+ ConfigUpdatedEvent event) {
+ Multimap<UpdateResult, ConfigUpdateEntry> updates = ArrayListMultimap.create();
for (GerritConfigListener configListener : configListeners) {
- result.addAll(configListener.configUpdated(event));
+ updates.putAll(configListener.configUpdated(event));
}
- return result;
+ return updates;
}
}
diff --git a/java/com/google/gerrit/server/project/CommentLinkProvider.java b/java/com/google/gerrit/server/project/CommentLinkProvider.java
index 56cf51e..4987d00 100644
--- a/java/com/google/gerrit/server/project/CommentLinkProvider.java
+++ b/java/com/google/gerrit/server/project/CommentLinkProvider.java
@@ -16,15 +16,17 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.server.config.ConfigUpdatedEvent;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
import com.google.gerrit.server.config.GerritConfigListener;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
@@ -64,11 +66,11 @@
}
@Override
- public List<ConfigUpdatedEvent.Update> configUpdated(ConfigUpdatedEvent event) {
+ public Multimap<UpdateResult, ConfigUpdateEntry> configUpdated(ConfigUpdatedEvent event) {
if (event.isSectionUpdated(ProjectConfig.COMMENTLINK)) {
commentLinks = parseConfig(event.getNewConfig());
- return Collections.singletonList(event.accept(ProjectConfig.COMMENTLINK));
+ return event.accept(ProjectConfig.COMMENTLINK);
}
- return Collections.emptyList();
+ return ConfigUpdatedEvent.NO_UPDATES;
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/ReloadConfig.java b/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
index de3c3ee..cab07e3 100644
--- a/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
+++ b/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
@@ -16,12 +16,12 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
+import com.google.common.collect.Multimap;
import com.google.gerrit.extensions.api.config.ConfigUpdateEntryInfo;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.config.ConfigResource;
-import com.google.gerrit.server.config.ConfigUpdatedEvent;
import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
import com.google.gerrit.server.config.GerritServerConfigReloader;
@@ -29,10 +29,11 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
public class ReloadConfig implements RestModifyView<ConfigResource, Input> {
@@ -49,25 +50,22 @@
public Map<String, List<ConfigUpdateEntryInfo>> apply(ConfigResource resource, Input input)
throws RestApiException, PermissionBackendException {
permissions.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
-
- List<ConfigUpdatedEvent.Update> updates = config.reloadConfig();
-
- Map<String, List<ConfigUpdateEntryInfo>> reply = new HashMap<>();
- for (UpdateResult result : UpdateResult.values()) {
- reply.put(result.name().toLowerCase(), new ArrayList<>());
- }
+ Multimap<UpdateResult, ConfigUpdateEntry> updates = config.reloadConfig();
if (updates.isEmpty()) {
- return reply;
+ return Collections.emptyMap();
}
- updates
+ return updates
+ .asMap()
+ .entrySet()
.stream()
- .forEach(u -> reply.get(u.getResult().name().toLowerCase()).addAll(toEntryInfos(u)));
- return reply;
+ .collect(
+ Collectors.toMap(
+ e -> e.getKey().name().toLowerCase(), e -> toEntryInfos(e.getValue())));
}
- private static List<ConfigUpdateEntryInfo> toEntryInfos(ConfigUpdatedEvent.Update update) {
- return update
- .getConfigUpdates()
+ private static List<ConfigUpdateEntryInfo> toEntryInfos(
+ Collection<ConfigUpdateEntry> updateEntries) {
+ return updateEntries
.stream()
.map(ReloadConfig::toConfigUpdateEntryInfo)
.collect(toImmutableList());
diff --git a/java/com/google/gerrit/server/restapi/project/SetParent.java b/java/com/google/gerrit/server/restapi/project/SetParent.java
index ca7e7aa..d02d04a 100644
--- a/java/com/google/gerrit/server/restapi/project/SetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/SetParent.java
@@ -19,6 +19,7 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
import com.google.gerrit.extensions.api.projects.ParentInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -32,6 +33,8 @@
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigKey;
import com.google.gerrit.server.config.ConfigUpdatedEvent;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
import com.google.gerrit.server.config.GerritConfigListener;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -46,8 +49,6 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
@@ -172,18 +173,18 @@
}
@Override
- public List<ConfigUpdatedEvent.Update> configUpdated(ConfigUpdatedEvent event) {
+ public Multimap<UpdateResult, ConfigUpdateEntry> configUpdated(ConfigUpdatedEvent event) {
ConfigKey receiveSetParent = ConfigKey.create("receive", "allowProjectOwnersToChangeParent");
if (!event.isValueUpdated(receiveSetParent)) {
- return Collections.emptyList();
+ return ConfigUpdatedEvent.NO_UPDATES;
}
try {
boolean enabled =
event.getNewConfig().getBoolean("receive", "allowProjectOwnersToChangeParent", false);
this.allowProjectOwnersToChangeParent = enabled;
- return Collections.singletonList(event.accept(receiveSetParent));
} catch (IllegalArgumentException iae) {
- return Collections.singletonList(event.reject(receiveSetParent));
+ return event.reject(receiveSetParent);
}
+ return event.accept(receiveSetParent);
}
}
diff --git a/java/com/google/gerrit/server/schema/SchemaCreator.java b/java/com/google/gerrit/server/schema/SchemaCreator.java
index 743019d..13734c3 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreator.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -20,7 +20,6 @@
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.Sequences;
@@ -150,7 +149,6 @@
GroupReference admins = createGroupReference("Administrators");
GroupReference batchUsers = createGroupReference("Non-Interactive Users");
- initSystemConfig(db);
allProjectsCreator.setAdministrators(admins).setBatchUsers(batchUsers).create();
// We have to create the All-Users repository before we can use it to store the groups in it.
allUsersCreator.setAdministrators(admins).create();
@@ -274,15 +272,4 @@
.setGroupUUID(groupReference.getUUID())
.build();
}
-
- private SystemConfig initSystemConfig(ReviewDb db) throws OrmException {
- SystemConfig s = SystemConfig.create();
- try {
- s.sitePath = site_path.toRealPath().normalize().toString();
- } catch (IOException e) {
- s.sitePath = site_path.toAbsolutePath().normalize().toString();
- }
- db.systemConfig().insert(Collections.singleton(s));
- return s;
- }
}
diff --git a/java/com/google/gerrit/server/schema/SchemaUpdater.java b/java/com/google/gerrit/server/schema/SchemaUpdater.java
index 266fbaa..5ead6aa 100644
--- a/java/com/google/gerrit/server/schema/SchemaUpdater.java
+++ b/java/com/google/gerrit/server/schema/SchemaUpdater.java
@@ -16,7 +16,6 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.GerritPersonIdent;
@@ -38,7 +37,6 @@
import com.google.inject.Stage;
import java.io.IOException;
import java.sql.SQLException;
-import java.util.Collections;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
@@ -46,18 +44,13 @@
/** Creates or updates the current database schema. */
public class SchemaUpdater {
private final SchemaFactory<ReviewDb> schema;
- private final SitePaths site;
private final SchemaCreator creator;
private final Provider<SchemaVersion> updater;
@Inject
SchemaUpdater(
- @ReviewDbFactory SchemaFactory<ReviewDb> schema,
- SitePaths site,
- SchemaCreator creator,
- Injector parent) {
+ @ReviewDbFactory SchemaFactory<ReviewDb> schema, SchemaCreator creator, Injector parent) {
this.schema = schema;
- this.site = site;
this.creator = creator;
this.updater = buildInjector(parent).getProvider(SchemaVersion.class);
}
@@ -119,8 +112,6 @@
} catch (SQLException e) {
throw new OrmException("Cannot upgrade schema", e);
}
-
- updateSystemConfig(db);
}
}
}
@@ -137,17 +128,4 @@
return null;
}
}
-
- private void updateSystemConfig(ReviewDb db) throws OrmException {
- final SystemConfig sc = db.systemConfig().get(new SystemConfig.Key());
- if (sc == null) {
- throw new OrmException("No record in system_config table");
- }
- try {
- sc.sitePath = site.site_path.toRealPath().normalize().toString();
- } catch (IOException e) {
- sc.sitePath = site.site_path.toAbsolutePath().normalize().toString();
- }
- db.systemConfig().update(Collections.singleton(sc));
- }
}
diff --git a/java/com/google/gerrit/server/schema/SchemaVersion.java b/java/com/google/gerrit/server/schema/SchemaVersion.java
index 61e9c92..44533c9 100644
--- a/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -36,7 +36,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_169> C = Schema_169.class;
+ public static final Class<Schema_170> C = Schema_170.class;
public static int getBinaryVersion() {
return guessVersion(C);
diff --git a/java/com/google/gerrit/server/schema/Schema_170.java b/java/com/google/gerrit/server/schema/Schema_170.java
new file mode 100644
index 0000000..c87fa3e
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/Schema_170.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2018 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.schema;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class Schema_170 extends SchemaVersion {
+ @Inject
+ Schema_170(Provider<Schema_169> prior) {
+ super(prior);
+ }
+}
diff --git a/java/com/google/gerrit/sshd/SshLog.java b/java/com/google/gerrit/sshd/SshLog.java
index 0e34889..df3242c 100644
--- a/java/com/google/gerrit/sshd/SshLog.java
+++ b/java/com/google/gerrit/sshd/SshLog.java
@@ -15,6 +15,7 @@
package com.google.gerrit.sshd;
import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.CurrentUser;
@@ -24,6 +25,8 @@
import com.google.gerrit.server.audit.SshAuditEvent;
import com.google.gerrit.server.config.ConfigKey;
import com.google.gerrit.server.config.ConfigUpdatedEvent;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
import com.google.gerrit.server.config.GerritConfigListener;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.ioutil.HexFormat;
@@ -33,8 +36,6 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.util.Collections;
-import java.util.List;
import org.apache.log4j.AsyncAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
@@ -318,25 +319,22 @@
}
@Override
- public List<ConfigUpdatedEvent.Update> configUpdated(ConfigUpdatedEvent event) {
+ public Multimap<UpdateResult, ConfigUpdateEntry> configUpdated(ConfigUpdatedEvent event) {
ConfigKey sshdRequestLog = ConfigKey.create("sshd", "requestLog");
if (!event.isValueUpdated(sshdRequestLog)) {
- return Collections.emptyList();
+ return ConfigUpdatedEvent.NO_UPDATES;
}
boolean stateUpdated;
try {
boolean enabled = event.getNewConfig().getBoolean("sshd", "requestLog", true);
-
if (enabled) {
stateUpdated = enableLogging();
} else {
stateUpdated = disableLogging();
}
- return stateUpdated
- ? Collections.singletonList(event.accept(sshdRequestLog))
- : Collections.emptyList();
+ return stateUpdated ? event.accept(sshdRequestLog) : ConfigUpdatedEvent.NO_UPDATES;
} catch (IllegalArgumentException iae) {
- return Collections.singletonList(event.reject(sshdRequestLog));
+ return event.reject(sshdRequestLog);
}
}
}
diff --git a/java/com/google/gerrit/sshd/commands/ReloadConfig.java b/java/com/google/gerrit/sshd/commands/ReloadConfig.java
index 1b21230..cbe3c57 100644
--- a/java/com/google/gerrit/sshd/commands/ReloadConfig.java
+++ b/java/com/google/gerrit/sshd/commands/ReloadConfig.java
@@ -16,16 +16,15 @@
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+import com.google.common.collect.Multimap;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
-import com.google.gerrit.server.config.ConfigUpdatedEvent;
+import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
import com.google.gerrit.server.config.GerritServerConfigReloader;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
-import java.util.List;
-import java.util.stream.Collectors;
/** Issues a reload of gerrit.config. */
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@@ -39,31 +38,16 @@
@Override
protected void run() throws Failure {
- List<ConfigUpdatedEvent.Update> updates = gerritServerConfigReloader.reloadConfig();
+ Multimap<UpdateResult, ConfigUpdateEntry> updates = gerritServerConfigReloader.reloadConfig();
if (updates.isEmpty()) {
stdout.println("No config entries updated!");
return;
}
// Print out UpdateResult.{ACCEPTED|REJECTED} entries grouped by their type
- for (UpdateResult updateResult : UpdateResult.values()) {
- List<ConfigUpdatedEvent.Update> filteredUpdates = filterUpdates(updates, updateResult);
- if (filteredUpdates.isEmpty()) {
- continue;
- }
- stdout.println(updateResult.toString() + " configuration changes:");
- filteredUpdates
- .stream()
- .flatMap(update -> update.getConfigUpdates().stream())
- .forEach(cfgEntry -> stdout.println(cfgEntry.toString()));
+ for (UpdateResult result : updates.keySet()) {
+ stdout.println(result.toString() + " configuration changes:");
+ updates.get(result).forEach(cfgEntry -> stdout.println(cfgEntry.toString()));
}
}
-
- public static List<ConfigUpdatedEvent.Update> filterUpdates(
- List<ConfigUpdatedEvent.Update> updates, UpdateResult result) {
- return updates
- .stream()
- .filter(update -> update.getResult() == result)
- .collect(Collectors.toList());
- }
}
diff --git a/java/com/google/gerrit/testing/DisabledReviewDb.java b/java/com/google/gerrit/testing/DisabledReviewDb.java
index d902e11..2bf95b0 100644
--- a/java/com/google/gerrit/testing/DisabledReviewDb.java
+++ b/java/com/google/gerrit/testing/DisabledReviewDb.java
@@ -21,7 +21,6 @@
import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.reviewdb.server.SchemaVersionAccess;
-import com.google.gerrit.reviewdb.server.SystemConfigAccess;
import com.google.gwtorm.server.Access;
import com.google.gwtorm.server.StatementExecutor;
@@ -71,11 +70,6 @@
}
@Override
- public SystemConfigAccess systemConfig() {
- throw new Disabled();
- }
-
- @Override
public ChangeAccess changes() {
throw new Disabled();
}
diff --git a/java/com/google/gerrit/testing/InMemoryDatabase.java b/java/com/google/gerrit/testing/InMemoryDatabase.java
index a3d7c17..66a5290 100644
--- a/java/com/google/gerrit/testing/InMemoryDatabase.java
+++ b/java/com/google/gerrit/testing/InMemoryDatabase.java
@@ -20,7 +20,6 @@
import com.google.gerrit.pgm.init.index.elasticsearch.ElasticIndexModuleOnInit;
import com.google.gerrit.pgm.init.index.lucene.LuceneIndexModuleOnInit;
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.schema.SchemaCreator;
@@ -127,12 +126,6 @@
return this;
}
- public SystemConfig getSystemConfig() throws OrmException {
- try (ReviewDb c = open()) {
- return c.systemConfig().get(new SystemConfig.Key());
- }
- }
-
public CurrentSchemaVersion getSchemaVersion() throws OrmException {
try (ReviewDb c = open()) {
return c.schemaVersion().get(new CurrentSchemaVersion.Key());
diff --git a/javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java b/javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java
index d3f69982..9569745 100644
--- a/javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java
@@ -31,7 +31,6 @@
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.File;
-import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -61,7 +60,7 @@
}
@Test
- public void getCauses_CreateSchema() throws OrmException, SQLException, IOException {
+ public void getCauses_CreateSchema() throws OrmException, SQLException {
// Initially the schema should be empty.
String[] types = {"TABLE", "VIEW"};
try (JdbcSchema d = (JdbcSchema) db.open();
@@ -80,7 +79,6 @@
if (sitePath.getName().equals(".")) {
sitePath = sitePath.getParentFile();
}
- assertThat(db.getSystemConfig().sitePath).isEqualTo(sitePath.getCanonicalPath());
}
private LabelTypes getLabelTypes() throws Exception {
diff --git a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index c4844b1..7ea4d93 100644
--- a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -20,7 +20,6 @@
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
@@ -146,7 +145,5 @@
u.update(new TestUpdateUI());
db.assertSchemaVersion();
- final SystemConfig sc = db.getSystemConfig();
- assertThat(sc.sitePath).isEqualTo(paths.site_path.toAbsolutePath().toString());
}
}
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
index d1fdf2f..af982cf 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
@@ -165,6 +165,7 @@
PREV_FILE: 'PREV_FILE',
NEXT_FILE_WITH_COMMENTS: 'NEXT_FILE_WITH_COMMENTS',
PREV_FILE_WITH_COMMENTS: 'PREV_FILE_WITH_COMMENTS',
+ NEXT_UNREVIEWED_FILE: 'NEXT_UNREVIEWED_FILE',
CURSOR_NEXT_FILE: 'CURSOR_NEXT_FILE',
CURSOR_PREV_FILE: 'CURSOR_PREV_FILE',
OPEN_FILE: 'OPEN_FILE',
@@ -255,6 +256,8 @@
'Mark/unmark file as reviewed');
_describe(Shortcut.TOGGLE_DIFF_MODE, ShortcutSection.DIFFS,
'Toggle unified/side-by-side diff');
+ _describe(Shortcut.NEXT_UNREVIEWED_FILE, ShortcutSection.DIFFS,
+ 'Mark file as reviewed and go to next unreviewed file');
_describe(Shortcut.NEXT_FILE, ShortcutSection.NAVIGATION, 'Select next file');
_describe(Shortcut.PREV_FILE, ShortcutSection.NAVIGATION,
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
index 5a463be..4073798 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
@@ -128,8 +128,17 @@
});
},
+ _refreshGroupsList() {
+ this.$.restAPI.invalidateGroupsCache(this._filter,
+ this._groupsPerPage, this._offset);
+ return this._getGroups(this._filter, this._groupsPerPage,
+ this._offset);
+ },
+
_handleCreateGroup() {
- this.$.createNewModal.handleCreateGroup();
+ this.$.createNewModal.handleCreateGroup().then(() => {
+ this._refreshGroupsList();
+ });
},
_handleCloseCreate() {
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
index ad12a44..987b63d 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
@@ -55,7 +55,7 @@
padding: 0 .15em;
}
}
- .hideBranch {
+ .hide {
display: none;
}
</style>
@@ -108,7 +108,7 @@
</iron-autogrow-textarea>
</span>
</section>
- <section>
+ <section class$="[[_computePrivateSectionClass(_privateChangesEnabled)]]">
<label
class="title"
for="privateChangeCheckBox">Private change</label>
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
index 826a6dc..8e15755 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
@@ -44,6 +44,7 @@
notify: true,
value: false,
},
+ _privateChangesEnabled: Boolean,
},
behaviors: [
@@ -52,10 +53,23 @@
],
attached() {
- if (!this.repoName) { return; }
- this.$.restAPI.getProjectConfig(this.repoName).then(config => {
- this.privateByDefault = config.private_by_default;
- });
+ if (!this.repoName) { return Promise.resolve(); }
+
+ const promises = [];
+
+ promises.push(this.$.restAPI.getProjectConfig(this.repoName)
+ .then(config => {
+ this.privateByDefault = config.private_by_default;
+ }));
+
+ promises.push(this.$.restAPI.getConfig().then(config => {
+ if (!config) { return; }
+
+ this._privateConfig = config && config.change &&
+ config.change.disable_private_changes;
+ }));
+
+ return Promise.all(promises);
},
observers: [
@@ -63,7 +77,7 @@
],
_computeBranchClass(baseChange) {
- return baseChange ? 'hideBranch' : '';
+ return baseChange ? 'hide' : '';
},
_allowCreate(branch, subject) {
@@ -120,5 +134,9 @@
return false;
}
},
+
+ _computePrivateSectionClass(config) {
+ return config ? 'hide' : '';
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
index 08c569c..aa4da68 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
@@ -158,5 +158,15 @@
done();
});
});
+
+ test('_computeBranchClass', () => {
+ assert.equal(element._computeBranchClass(true), 'hide');
+ assert.equal(element._computeBranchClass(false), '');
+ });
+
+ test('_computePrivateSectionClass', () => {
+ assert.equal(element._computePrivateSectionClass(true), 'hide');
+ assert.equal(element._computePrivateSectionClass(false), '');
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
index 36a7d76..1d49db9 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
@@ -53,7 +53,7 @@
</tr>
<template is="dom-repeat" items="[[item.dashboards]]">
<tr class="table">
- <td class="name"><a href$="[[_getUrl(item.project, item.sections)]]">[[item.path]]</a></td>
+ <td class="name"><a href$="[[_getUrl(item.project, item.id)]]">[[item.path]]</a></td>
<td class="title">[[item.title]]</td>
<td class="desc">[[item.description]]</td>
<td class="inherited">[[_computeInheritedFrom(item.project, item.defining_project)]]</td>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
index c0fc0cb..7dea7f4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
@@ -43,24 +43,24 @@
this.$.restAPI.getRepoDashboards(this.repo, errFn).then(res => {
if (!res) { return Promise.resolve(); }
- // Flatten 2 dimenional array, and sort by id.
+ // Group by ref and sort by id.
const dashboards = res.concat.apply([], res).sort((a, b) =>
- a.id > b.id);
- const customList = dashboards.filter(a => a.ref === 'custom');
- const defaultList = dashboards.filter(a => a.ref === 'default');
+ a.id < b.id ? -1 : 1);
+ const dashboardsByRef = {};
+ dashboards.forEach(d => {
+ if (!dashboardsByRef[d.ref]) {
+ dashboardsByRef[d.ref] = [];
+ }
+ dashboardsByRef[d.ref].push(d);
+ });
+
const dashboardBuilder = [];
- if (customList.length) {
+ Object.keys(dashboardsByRef).sort().forEach(ref => {
dashboardBuilder.push({
- section: 'Custom',
- dashboards: customList,
+ section: ref,
+ dashboards: dashboardsByRef[ref],
});
- }
- if (defaultList.length) {
- dashboardBuilder.push({
- section: 'Default',
- dashboards: defaultList,
- });
- }
+ });
this._dashboards = dashboardBuilder;
this._loading = false;
@@ -68,10 +68,10 @@
});
},
- _getUrl(project, sections) {
- if (!project || !sections) { return ''; }
+ _getUrl(project, id) {
+ if (!project || !id) { return ''; }
- return Gerrit.Nav.getUrlForCustomDashboard(project, sections);
+ return Gerrit.Nav.getUrlForRepoDashboard(project, id);
},
_computeLoadingClass(loading) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
index 4d86a0c..94bf5e0 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
@@ -46,54 +46,61 @@
sandbox.restore();
});
- suite('with default only', () => {
+ suite('dashboard table', () => {
setup(() => {
sandbox.stub(element.$.restAPI, 'getRepoDashboards').returns(
Promise.resolve([
- [
- {
- id: 'default:contributor',
- project: 'gerrit',
- defining_project: 'gerrit',
- ref: 'default',
- path: 'contributor',
- description: 'Own contributions.',
- foreach: 'owner:self',
- url: '/dashboard/?params',
- title: 'Contributor Dashboard',
- sections: [
- {
- name: 'Mine To Rebase',
- query: 'is:open -is:mergeable',
- },
- {
- name: 'My Recently Merged',
- query: 'is:merged limit:10',
- },
- ],
- },
- ],
- [
- {
- id: 'default:open',
- project: 'gerrit',
- defining_project: 'Public-Projects',
- ref: 'default',
- path: 'open',
- description: 'Recent open changes.',
- url: '/dashboard/?params',
- title: 'Open Changes',
- sections: [
- {
- name: 'Open Changes',
- query: 'status:open project:${project} -age:7w',
- },
- ],
- },
- ],
+ {
+ id: 'default:contributor',
+ project: 'gerrit',
+ defining_project: 'gerrit',
+ ref: 'default',
+ path: 'contributor',
+ description: 'Own contributions.',
+ foreach: 'owner:self',
+ url: '/dashboard/?params',
+ title: 'Contributor Dashboard',
+ sections: [
+ {
+ name: 'Mine To Rebase',
+ query: 'is:open -is:mergeable',
+ },
+ {
+ name: 'My Recently Merged',
+ query: 'is:merged limit:10',
+ },
+ ],
+ },
+ {
+ id: 'custom:custom2',
+ project: 'gerrit',
+ defining_project: 'Public-Projects',
+ ref: 'custom',
+ path: 'open',
+ description: 'Recent open changes.',
+ url: '/dashboard/?params',
+ title: 'Open Changes',
+ sections: [
+ {
+ name: 'Open Changes',
+ query: 'status:open project:${project} -age:7w',
+ },
+ ],
+ },
+ {
+ id: 'default:abc',
+ project: 'gerrit',
+ ref: 'default',
+ },
+ {
+ id: 'custom:custom1',
+ project: 'gerrit',
+ ref: 'custom',
+ },
]));
});
- test('loading', done => {
+
+ test('loading, sections, and ordering', done => {
assert.isTrue(element._loading);
assert.notEqual(getComputedStyle(element.$.loadingContainer).display,
'none');
@@ -101,143 +108,20 @@
'none');
element.repo = 'test';
flush(() => {
- assert.equal(element._dashboards.length, 1);
- assert.equal(element._dashboards[0].section, 'Default');
- assert.equal(element._dashboards[0].dashboards.length, 2);
assert.equal(getComputedStyle(element.$.loadingContainer).display,
'none');
assert.notEqual(getComputedStyle(element.$.dashboards).display,
'none');
- done();
- });
- });
- test('dispatched command-tap on button tap', done => {
- element.repo = 'test';
- flush(() => {
- assert.equal(element._dashboards.length, 1);
- assert.equal(element._dashboards[0].section, 'Default');
- assert.equal(element._dashboards[0].dashboards.length, 2);
- done();
- });
- });
- });
-
- suite('with custom only', () => {
- setup(() => {
- sandbox.stub(element.$.restAPI, 'getRepoDashboards').returns(
- Promise.resolve([
- [
- {
- id: 'custom:custom1',
- project: 'gerrit',
- defining_project: 'gerrit',
- ref: 'custom',
- path: 'contributor',
- description: 'Own contributions.',
- foreach: 'owner:self',
- url: '/dashboard/?params',
- title: 'Contributor Dashboard',
- sections: [
- {
- name: 'Mine To Rebase',
- query: 'is:open -is:mergeable',
- },
- {
- name: 'My Recently Merged',
- query: 'is:merged limit:10',
- },
- ],
- },
- ],
- [
- {
- id: 'custom:custom2',
- project: 'gerrit',
- defining_project: 'Public-Projects',
- ref: 'custom',
- path: 'open',
- description: 'Recent open changes.',
- url: '/dashboard/?params',
- title: 'Open Changes',
- sections: [
- {
- name: 'Open Changes',
- query: 'status:open project:${project} -age:7w',
- },
- ],
- },
- ],
- ]));
- });
-
- test('dispatched command-tap on button tap', done => {
- element.repo = 'test';
- flush(() => {
- assert.equal(element._dashboards.length, 1);
- assert.equal(element._dashboards[0].section, 'Custom');
- assert.equal(element._dashboards[0].dashboards.length, 2);
- done();
- });
- });
- });
-
- suite('with custom and default', () => {
- setup(() => {
- sandbox.stub(element.$.restAPI, 'getRepoDashboards').returns(
- Promise.resolve([
- [
- {
- id: 'default:contributor',
- project: 'gerrit',
- defining_project: 'gerrit',
- ref: 'default',
- path: 'contributor',
- description: 'Own contributions.',
- foreach: 'owner:self',
- url: '/dashboard/?params',
- title: 'Contributor Dashboard',
- sections: [
- {
- name: 'Mine To Rebase',
- query: 'is:open -is:mergeable',
- },
- {
- name: 'My Recently Merged',
- query: 'is:merged limit:10',
- },
- ],
- },
- ],
- [
- {
- id: 'custom:custom2',
- project: 'gerrit',
- defining_project: 'Public-Projects',
- ref: 'custom',
- path: 'open',
- description: 'Recent open changes.',
- url: '/dashboard/?params',
- title: 'Open Changes',
- sections: [
- {
- name: 'Open Changes',
- query: 'status:open project:${project} -age:7w',
- },
- ],
- },
- ],
- ]));
- });
-
- test('dispatched command-tap on button tap', done => {
- element.repo = 'test';
- flush(() => {
assert.equal(element._dashboards.length, 2);
- assert.equal(element._dashboards[0].section, 'Custom');
- assert.equal(element._dashboards[1].section, 'Default');
- assert.equal(element._dashboards[0].dashboards.length, 1);
- assert.equal(element._dashboards[1].dashboards.length, 1);
+ assert.equal(element._dashboards[0].section, 'custom');
+ assert.equal(element._dashboards[1].section, 'default');
+
+ const dashboards = element._dashboards[0].dashboards;
+ assert.equal(dashboards.length, 2);
+ assert.equal(dashboards[0].id, 'custom:custom1');
+ assert.equal(dashboards[1].id, 'custom:custom2');
+
done();
});
});
@@ -245,7 +129,7 @@
suite('test url', () => {
test('_getUrl', () => {
- sandbox.stub(Gerrit.Nav, 'getUrlForCustomDashboard',
+ sandbox.stub(Gerrit.Nav, 'getUrlForRepoDashboard',
() => '/r/dashboard/test');
assert.equal(element._getUrl('/dashboard/test', {}), '/r/dashboard/test');
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
index 4b82e57..116f084 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
@@ -129,8 +129,17 @@
});
},
+ _refreshReposList() {
+ this.$.restAPI.invalidateReposCache(this._filter,
+ this._reposPerPage, this._offset);
+ return this._getRepos(this._filter, this._reposPerPage,
+ this._offset);
+ },
+
_handleCreateRepo() {
- this.$.createNewModal.handleCreateRepo();
+ this.$.createNewModal.handleCreateRepo().then(() => {
+ this._refreshReposList();
+ });
},
_handleCloseCreate() {
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 99aa265..b0ba8a2b 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
@@ -87,7 +87,7 @@
<div hidden$="[[_loading]]" hidden>
<gr-user-header
user-id="[[params.user]]"
- class$="[[_computeUserHeaderClass(params.user)]]"></gr-user-header>
+ class$="[[_computeUserHeaderClass(params)]]"></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 775c046..56bc17c 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
@@ -37,7 +37,7 @@
/** @type {{ selectedChangeIndex: number }} */
viewState: Object,
- /** @type {{ user: string }} */
+ /** @type {{ project: string, user: string }} */
params: {
type: Object,
},
@@ -217,8 +217,12 @@
});
},
- _computeUserHeaderClass(userParam) {
- return userParam === 'self' ? 'hide' : '';
+ _computeUserHeaderClass(params) {
+ if (!params || !!params.project || !params.user
+ || params.user === 'self') {
+ return 'hide';
+ }
+ return '';
},
_handleToggleStar(e) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
index de74218..3a3454d 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
@@ -314,10 +314,13 @@
});
test('_computeUserHeaderClass', () => {
- assert.equal(element._computeUserHeaderClass(undefined), '');
- assert.equal(element._computeUserHeaderClass(''), '');
- assert.equal(element._computeUserHeaderClass('self'), 'hide');
- assert.equal(element._computeUserHeaderClass('user'), '');
+ assert.equal(element._computeUserHeaderClass(undefined), 'hide');
+ assert.equal(element._computeUserHeaderClass({}), 'hide');
+ assert.equal(element._computeUserHeaderClass({user: 'self'}), 'hide');
+ assert.equal(element._computeUserHeaderClass({user: 'user'}), '');
+ assert.equal(
+ element._computeUserHeaderClass({project: 'p', user: 'user'}),
+ 'hide');
});
test('404 page', done => {
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index 6509bb1..bec08a8 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -27,6 +27,7 @@
<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
+<link rel="import" href="../../shared/gr-icons/gr-icons.html">
<link rel="import" href="../../shared/gr-limited-text/gr-limited-text.html">
<link rel="import" href="../../shared/gr-linked-chip/gr-linked-chip.html">
<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
@@ -114,6 +115,19 @@
#parentNotCurrentMessage {
display: none;
}
+ .icon {
+ margin: -.25em 0;
+ }
+ .icon.help,
+ .icon.notTrusted {
+ color: #FFA62F;
+ }
+ .icon.invalid {
+ color: var(--vote-text-color-disliked);
+ }
+ .icon.trusted {
+ color: var(--vote-text-color-recommended);
+ }
.parentList.notCurrent.nonMerge #parentNotCurrentMessage {
--arrow-color: #ffa62f;
display: inline-block;
@@ -137,13 +151,40 @@
<span class="title">Owner</span>
<span class="value">
<gr-account-link account="[[change.owner]]"></gr-account-link>
+ <template is="dom-if" if="[[_pushCertificateValidation]]">
+ <gr-tooltip-content
+ has-tooltip
+ title$="[[_pushCertificateValidation.message]]">
+ <iron-icon
+ class$="icon [[_pushCertificateValidation.class]]"
+ icon="[[_pushCertificateValidation.icon]]">
+ </iron-icon>
+ </gr-tooltip-content>
+ </template>
</span>
</section>
- <section class$="[[_computeShowUploaderHide(change)]]">
+ <section class$="[[_computeShowRoleClass(change, _CHANGE_ROLE.UPLOADER)]]">
<span class="title">Uploader</span>
<span class="value">
<gr-account-link
- account="[[_computeShowUploader(change)]]"></gr-account-link>
+ account="[[_getNonOwnerRole(change, _CHANGE_ROLE.UPLOADER)]]"
+ ></gr-account-link>
+ </span>
+ </section>
+ <section class$="[[_computeShowRoleClass(change, _CHANGE_ROLE.AUTHOR)]]">
+ <span class="title">Author</span>
+ <span class="value">
+ <gr-account-link
+ account="[[_getNonOwnerRole(change, _CHANGE_ROLE.AUTHOR)]]"
+ ></gr-account-link>
+ </span>
+ </section>
+ <section class$="[[_computeShowRoleClass(change, _CHANGE_ROLE.COMMITTER)]]">
+ <span class="title">Committer</span>
+ <span class="value">
+ <gr-account-link
+ account="[[_getNonOwnerRole(change, _CHANGE_ROLE.COMMITTER)]]"
+ ></gr-account-link>
</span>
</section>
<section class="assignee">
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index 8d1546b..d3fc7e0 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -17,6 +17,17 @@
(function() {
'use strict';
+ const Defs = {};
+
+ /**
+ * @typedef {{
+ * message: string,
+ * icon: string,
+ * class: string,
+ * }}
+ */
+ Defs.PushCertificateValidation;
+
const HASHTAG_ADD_MESSAGE = 'Add Hashtag';
const SubmitTypeLabel = {
@@ -30,6 +41,24 @@
const NOT_CURRENT_MESSAGE = 'Not current - rebase possible';
+ /**
+ * @enum {string}
+ */
+ const CertificateStatus = {
+ /**
+ * This certificate status is bad.
+ */
+ BAD: 'BAD',
+ /**
+ * This certificate status is OK.
+ */
+ OK: 'OK',
+ /**
+ * This certificate status is TRUSTED.
+ */
+ TRUSTED: 'TRUSTED',
+ };
+
Polymer({
is: 'gr-change-metadata',
@@ -76,6 +105,13 @@
type: Boolean,
computed: '_computeShowReviewersByState(serverConfig)',
},
+ /**
+ * @type {Defs.PushCertificateValidation}
+ */
+ _pushCertificateValidation: {
+ type: Object,
+ computed: '_computePushCertificateValidation(serverConfig, change)',
+ },
_showRequirements: {
type: Boolean,
computed: '_computeShowRequirements(change)',
@@ -97,6 +133,18 @@
type: Array,
computed: '_computeParents(change)',
},
+
+ /** @type {?} */
+ _CHANGE_ROLE: {
+ type: Object,
+ readOnly: true,
+ value: {
+ OWNER: 'owner',
+ UPLOADER: 'uploader',
+ AUTHOR: 'author',
+ COMMITTER: 'committer',
+ },
+ },
},
behaviors: [
@@ -248,6 +296,59 @@
return hasRequirements || hasLabels || !!change.work_in_progress;
},
+ /**
+ * @return {?Defs.PushCertificateValidation} object representing data for
+ * the push validation.
+ */
+ _computePushCertificateValidation(serverConfig, change) {
+ if (!serverConfig || !serverConfig.receive ||
+ !serverConfig.receive.enable_signed_push) {
+ return null;
+ }
+ const rev = change.revisions[change.current_revision];
+ if (!rev.push_certificate || !rev.push_certificate.key) {
+ return {
+ class: 'help',
+ icon: 'gr-icons:help',
+ message: 'This patch set was created without a push certificate',
+ };
+ }
+
+ const key = rev.push_certificate.key;
+ switch (key.status) {
+ case CertificateStatus.BAD:
+ return {
+ class: 'invalid',
+ icon: 'gr-icons:close',
+ message: this._problems('Push certificate is invalid', key),
+ };
+ case CertificateStatus.OK:
+ return {
+ class: 'notTrusted',
+ icon: 'gr-icons:info',
+ message: this._problems(
+ 'Push certificate is valid, but key is not trusted', key),
+ };
+ case CertificateStatus.TRUSTED:
+ return {
+ class: 'trusted',
+ icon: 'gr-icons:check',
+ message: this._problems(
+ 'Push certificate is valid and key is trusted', key),
+ };
+ default:
+ throw new Error(`unknown certificate status: ${key.status}`);
+ }
+ },
+
+ _problems(msg, key) {
+ if (!key || !key.problems || key.problems.length === 0) {
+ return msg;
+ }
+
+ return [msg + ':'].concat(key.problems).join('\n');
+ },
+
_computeProjectURL(project) {
return Gerrit.Nav.getUrlForProjectChanges(project);
},
@@ -299,24 +400,45 @@
return !!change.work_in_progress;
},
- _computeShowUploaderHide(change) {
- return this._computeShowUploader(change) ? '' : 'hideDisplay';
+ _computeShowRoleClass(change, role) {
+ return this._getNonOwnerRole(change, role) ? '' : 'hideDisplay';
},
- _computeShowUploader(change) {
+ /**
+ * Get the user with the specified role on the change. Returns null if the
+ * user with that role is the same as the owner.
+ * @param {!Object} change
+ * @param {string} role One of the values from _CHANGE_ROLE
+ * @return {Object|null} either an accound or null.
+ */
+ _getNonOwnerRole(change, role) {
if (!change.current_revision ||
!change.revisions[change.current_revision]) {
return null;
}
const rev = change.revisions[change.current_revision];
+ if (!rev) { return null; }
- if (!rev || !rev.uploader ||
- change.owner._account_id === rev.uploader._account_id) {
- return null;
+ if (role === this._CHANGE_ROLE.UPLOADER &&
+ rev.uploader &&
+ change.owner._account_id !== rev.uploader._account_id) {
+ return rev.uploader;
}
- return rev.uploader;
+ if (role === this._CHANGE_ROLE.AUTHOR &&
+ rev.commit && rev.commit.author &&
+ change.owner.email !== rev.commit.author.email) {
+ return rev.commit.author;
+ }
+
+ if (role === this._CHANGE_ROLE.COMMITTER &&
+ rev.commit && rev.commit.committer &&
+ change.owner.email !== rev.commit.committer.email) {
+ return rev.commit.committer;
+ }
+
+ return null;
},
_computeParents(change) {
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
index af25d91..c5a569e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
@@ -185,7 +185,138 @@
assert.equal(element._computeWebLinks(element.commitInfo).length, 1);
});
- test('_computeShowUploader test for uploader', () => {
+ suite('_getNonOwnerRole', () => {
+ let change;
+
+ setup(() => {
+ change = {
+ owner: {
+ email: 'abc@def',
+ _account_id: 1019328,
+ },
+ revisions: {
+ rev1: {
+ _number: 1,
+ uploader: {
+ email: 'ghi@def',
+ _account_id: 1011123,
+ },
+ commit: {
+ author: {email: 'jkl@def'},
+ committer: {email: 'ghi@def'},
+ },
+ },
+ },
+ current_revision: 'rev1',
+ };
+ });
+
+ suite('role=uploader', () => {
+ test('_getNonOwnerRole for uploader', () => {
+ assert.deepEqual(
+ element._getNonOwnerRole(change, element._CHANGE_ROLE.UPLOADER),
+ {email: 'ghi@def', _account_id: 1011123});
+ });
+
+ test('_getNonOwnerRole that it does not return uploader', () => {
+ // Set the uploader email to be the same as the owner.
+ change.revisions.rev1.uploader._account_id = 1019328;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.UPLOADER));
+ });
+
+ test('_getNonOwnerRole null for uploader with no current rev', () => {
+ delete change.current_revision;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.UPLOADER));
+ });
+
+ test('_computeShowRoleClass show uploader', () => {
+ assert.equal(element._computeShowRoleClass(
+ change, element._CHANGE_ROLE.UPLOADER), '');
+ });
+
+ test('_computeShowRoleClass hide uploader', () => {
+ // Set the uploader email to be the same as the owner.
+ change.revisions.rev1.uploader._account_id = 1019328;
+ assert.equal(element._computeShowRoleClass(change,
+ element._CHANGE_ROLE.UPLOADER), 'hideDisplay');
+ });
+ });
+
+ suite('role=committer', () => {
+ test('_getNonOwnerRole for committer', () => {
+ assert.deepEqual(
+ element._getNonOwnerRole(change, element._CHANGE_ROLE.COMMITTER),
+ {email: 'ghi@def'});
+ });
+
+ test('_getNonOwnerRole that it does not return committer', () => {
+ // Set the committer email to be the same as the owner.
+ change.revisions.rev1.commit.committer.email = 'abc@def';
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.COMMITTER));
+ });
+
+ test('_getNonOwnerRole null for committer with no current rev', () => {
+ delete change.current_revision;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.COMMITTER));
+ });
+
+ test('_getNonOwnerRole null for committer with no commit', () => {
+ delete change.revisions.rev1.commit;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.COMMITTER));
+ });
+
+ test('_getNonOwnerRole null for committer with no committer', () => {
+ delete change.revisions.rev1.commit.committer;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.COMMITTER));
+ });
+ });
+
+ suite('role=author', () => {
+ test('_getNonOwnerRole for author', () => {
+ assert.deepEqual(
+ element._getNonOwnerRole(change, element._CHANGE_ROLE.AUTHOR),
+ {email: 'jkl@def'});
+ });
+
+ test('_getNonOwnerRole that it does not return author', () => {
+ // Set the author email to be the same as the owner.
+ change.revisions.rev1.commit.author.email = 'abc@def';
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.AUTHOR));
+ });
+
+ test('_getNonOwnerRole null for author with no current rev', () => {
+ delete change.current_revision;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.AUTHOR));
+ });
+
+ test('_getNonOwnerRole null for author with no commit', () => {
+ delete change.revisions.rev1.commit;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.AUTHOR));
+ });
+
+ test('_getNonOwnerRole null for author with no author', () => {
+ delete change.revisions.rev1.commit.author;
+ assert.isNull(element._getNonOwnerRole(change,
+ element._CHANGE_ROLE.AUTHOR));
+ });
+ });
+ });
+
+ test('Push Certificate Validation test BAD', () => {
+ const serverConfig = {
+ receive: {
+ enable_signed_push: true,
+ },
+ };
const change = {
change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
owner: {
@@ -194,8 +325,13 @@
revisions: {
rev1: {
_number: 1,
- uploader: {
- _account_id: 1011123,
+ push_certificate: {
+ key: {
+ status: 'BAD',
+ problems: [
+ 'No public keys found for key ID E5E20E52',
+ ],
+ },
},
},
},
@@ -204,54 +340,21 @@
labels: {},
mergeable: true,
};
- assert.deepEqual(element._computeShowUploader(change),
- {_account_id: 1011123});
+ const result =
+ element._computePushCertificateValidation(serverConfig, change);
+ assert.equal(result.message,
+ 'Push certificate is invalid:\n' +
+ 'No public keys found for key ID E5E20E52');
+ assert.equal(result.icon, 'gr-icons:close');
+ assert.equal(result.class, 'invalid');
});
- test('_computeShowUploader test that it does not return uploader', () => {
- const change = {
- change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
- owner: {
- _account_id: 1011123,
+ test('Push Certificate Validation test TRUSTED', () => {
+ const serverConfig = {
+ receive: {
+ enable_signed_push: true,
},
- revisions: {
- rev1: {
- _number: 1,
- uploader: {
- _account_id: 1011123,
- },
- },
- },
- current_revision: 'rev1',
- status: 'NEW',
- labels: {},
- mergeable: true,
};
- assert.isNotOk(element._computeShowUploader(change));
- });
-
- test('no current_revision makes _computeShowUploader return null', () => {
- const change = {
- change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
- owner: {
- _account_id: 1011123,
- },
- revisions: {
- rev1: {
- _number: 1,
- uploader: {
- _account_id: 1011123,
- },
- },
- },
- status: 'NEW',
- labels: {},
- mergeable: true,
- };
- assert.isNotOk(element._computeShowUploader(change));
- });
-
- test('_computeShowUploaderHide test for string which equals true', () => {
const change = {
change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
owner: {
@@ -260,8 +363,10 @@
revisions: {
rev1: {
_number: 1,
- uploader: {
- _account_id: 1011123,
+ push_certificate: {
+ key: {
+ status: 'TRUSTED',
+ },
},
},
},
@@ -270,21 +375,28 @@
labels: {},
mergeable: true,
};
- assert.equal(element._computeShowUploaderHide(change), '');
+ const result =
+ element._computePushCertificateValidation(serverConfig, change);
+ assert.equal(result.message,
+ 'Push certificate is valid and key is trusted');
+ assert.equal(result.icon, 'gr-icons:check');
+ assert.equal(result.class, 'trusted');
});
- test('_computeShowUploaderHide test for hideDisplay', () => {
+ test('Push Certificate Validation is missing test', () => {
+ const serverConfig = {
+ receive: {
+ enable_signed_push: true,
+ },
+ };
const change = {
change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
owner: {
- _account_id: 1011123,
+ _account_id: 1019328,
},
revisions: {
rev1: {
_number: 1,
- uploader: {
- _account_id: 1011123,
- },
},
},
current_revision: 'rev1',
@@ -292,8 +404,12 @@
labels: {},
mergeable: true,
};
- assert.equal(
- element._computeShowUploaderHide(change), 'hideDisplay');
+ const result =
+ element._computePushCertificateValidation(serverConfig, change);
+ assert.equal(result.message,
+ 'This patch set was created without a push certificate');
+ assert.equal(result.icon, 'gr-icons:help');
+ assert.equal(result.class, 'help');
});
test('_computeParents', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index 4296bd4..f6acef6 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -575,6 +575,7 @@
};
element._change = {
change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
+ owner: {email: 'abc@def'},
revisions: {
rev2: {_number: 2, commit: {parents: []}},
rev1: {_number: 1, commit: {parents: []}},
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index 0c3f887..3650707 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -517,14 +517,15 @@
/**
* @param {string} repo The name of the repo.
- * @param {!Array} sections The sections to display in the dashboard
+ * @param {string} dashboard The ID of the dashboard, in the form of
+ * '<ref>:<path>'.
* @return {string}
*/
- getUrlForCustomDashboard(repo, sections) {
+ getUrlForRepoDashboard(repo, dashboard) {
return this._getUrlFor({
- repo,
view: Gerrit.Nav.View.DASHBOARD,
- sections,
+ repo,
+ dashboard,
});
},
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 9ceb3d6..2a62115 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -324,14 +324,10 @@
if (!weblinks || !weblinks.length) return [];
return weblinks.filter(weblink => !this._isDirectCommit(weblink)).map(
({name, url}) => {
- if (url.startsWith('https:') || url.startsWith('http:')) {
- return {name, url};
- } else {
- return {
- name,
- url: `../../${url}`,
- };
+ if (!url.startsWith('https:') && !url.startsWith('http:')) {
+ url = this.getBaseUrl() + (url.startsWith('/') ? '' : '/') + url;
}
+ return {name, url};
});
},
@@ -424,7 +420,8 @@
return `/dashboard/${user}?${queryParams.join('&')}`;
} else if (repoName) {
// Project dashboard.
- return `/p/${repoName}/+/dashboard/${params.dashboard}`;
+ const encodedRepo = this.encodeURL(repoName, true);
+ return `/p/${encodedRepo}/+/dashboard/${params.dashboard}`;
} else {
// User dashboard.
return `/dashboard/${params.user || 'self'}`;
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 763e162..f221706 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
@@ -44,6 +44,23 @@
teardown(() => { sandbox.restore(); });
+ test('_getChangeWeblinks', () => {
+ sandbox.stub(element, '_isDirectCommit').returns(false);
+ sandbox.stub(element, 'getBaseUrl').returns('base');
+ const link = {name: 'test', url: 'test/url'};
+ const mapLinksToConfig = weblink => ({options: {weblinks: [weblink]}});
+ assert.deepEqual(element._getChangeWeblinks(mapLinksToConfig(link))[0],
+ {name: 'test', url: 'base/test/url'});
+
+ link.url = '/' + link.url;
+ assert.deepEqual(element._getChangeWeblinks(mapLinksToConfig(link))[0],
+ {name: 'test', url: 'base/test/url'});
+
+ link.url = 'https:/' + link.url;
+ assert.deepEqual(element._getChangeWeblinks(mapLinksToConfig(link))[0],
+ {name: 'test', url: 'https://test/url'});
+ });
+
test('_getHashFromCanonicalPath', () => {
let url = '/foo/bar';
let hash = element._getHashFromCanonicalPath(url);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index 6f361d8..cf8417a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -161,6 +161,10 @@
type: Object,
computed: '_getRevisionInfo(_change)',
},
+ _reviewedFiles: {
+ type: Object,
+ value: () => new Set(),
+ },
},
behaviors: [
@@ -207,6 +211,7 @@
[this.Shortcut.TOGGLE_DIFF_MODE]: '_handleToggleDiffMode',
[this.Shortcut.TOGGLE_FILE_REVIEWED]: '_handleToggleFileReviewed',
[this.Shortcut.EXPAND_ALL_DIFF_CONTEXT]: '_handleExpandAllDiffContext',
+ [this.Shortcut.NEXT_UNREVIEWED_FILE]: '_handleNextUnreviewedFile',
// Final two are actually handled by gr-diff-comment-thread.
[this.Shortcut.EXPAND_ALL_COMMENT_THREADS]: null,
@@ -555,10 +560,18 @@
return {path: fileList[idx]};
},
+ _getReviewedFiles(changeNum, patchNum) {
+ return this.$.restAPI.getReviewedFiles(changeNum, patchNum)
+ .then(files => {
+ this._reviewedFiles = new Set(files);
+ return this._reviewedFiles;
+ });
+ },
+
_getReviewedStatus(editMode, changeNum, patchNum, path) {
if (editMode) { return Promise.resolve(false); }
- return this.$.restAPI.getReviewedFiles(changeNum, patchNum)
- .then(files => files.includes(path));
+ return this._getReviewedFiles(changeNum, patchNum)
+ .then(files => files.has(path));
},
_paramsChanged(value) {
@@ -1012,5 +1025,15 @@
if (this.shouldSuppressKeyboardShortcut(e)) { return; }
this.$.diffHost.expandAllContext();
},
+
+ _handleNextUnreviewedFile(e) {
+ this._setReviewed(true);
+ // Ensure that the currently viewed file always appears in unreviewedFiles
+ // so we resolve the right "next" file.
+ const unreviewedFiles = this._fileList
+ .filter(file =>
+ (file === this._path || !this._reviewedFiles.has(file)));
+ this._navToFile(this._path, unreviewedFiles, 1);
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
index 431578b..958acdb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
@@ -67,6 +67,7 @@
kb.bindShortcut(kb.Shortcut.EXPAND_ALL_DIFF_CONTEXT, 'shift+x');
kb.bindShortcut(kb.Shortcut.EXPAND_ALL_COMMENT_THREADS, 'e');
kb.bindShortcut(kb.Shortcut.COLLAPSE_ALL_COMMENT_THREADS, 'shift+e');
+ kb.bindShortcut(kb.Shortcut.NEXT_UNREVIEWED_FILE, 'shift+m');
let element;
let sandbox;
@@ -1106,5 +1107,22 @@
assert.isTrue(setStub.calledOnce);
assert.isTrue(setStub.calledWith(101, 'test-project'));
});
+
+ test('shift+m navigates to next unreviewed file', () => {
+ element._fileList = ['file1', 'file2', 'file3'];
+ element._reviewedFiles = new Set(['file1', 'file2']);
+ element._path = 'file1';
+ const reviewedStub = sandbox.stub(element, '_setReviewed');
+ const navStub = sandbox.stub(element, '_navToFile');
+ MockInteractions.pressAndReleaseKeyOn(element, 77, 'shift', 'm');
+ flushAsynchronousOperations();
+
+ assert.isTrue(reviewedStub.lastCall.args[0]);
+ assert.deepEqual(navStub.lastCall.args, [
+ 'file1',
+ ['file1', 'file3'],
+ 1,
+ ]);
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 0cf517d..321dc58 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -275,6 +275,8 @@
this.bindShortcut(
this.Shortcut.TOGGLE_FILE_REVIEWED, 'r');
this.bindShortcut(
+ this.Shortcut.NEXT_UNREVIEWED_FILE, 'shift+m');
+ this.bindShortcut(
this.Shortcut.TOGGLE_ALL_INLINE_DIFFS, 'shift+i:keyup');
this.bindShortcut(
this.Shortcut.TOGGLE_INLINE_DIFF, 'i:keyup');
diff --git a/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html b/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
index 4f80475..4ea8cc7 100644
--- a/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
+++ b/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
@@ -48,6 +48,10 @@
<g id="publishEdit"><path d="M5 4v2h14V4H5zm0 10h4v6h6v-6h4l-7-7-7 7z"/></g>
<!-- This SVG is a copy from iron-icons https://github.com/PolymerElements/iron-icons/blob/master/editor-icons.html -->
<g id="delete"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></g>
+ <!-- This SVG is a copy from iron-icons https://github.com/PolymerElements/iron-icons/blob/master/iron-icons.js -->
+ <g id="help"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"></path></g>
+ <!-- This SVG is a copy from iron-icons https://github.com/PolymerElements/iron-icons/blob/master/iron-icons.js -->
+ <g id="info"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></g>
<!-- This SVG is a copy from material.io https://material.io/icons/#ic_hourglass_full-->
<g id="hourglass"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6z"/><path d="M0 0h24v24H0V0z" fill="none"/></g>
<!-- This is a custom PolyGerrit SVG -->
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
index 2fe5f7b1..40edc28 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
@@ -127,27 +127,8 @@
this.$.restAPI.deleteVote(this.change._number, accountID, this.label)
.then(response => {
target.disabled = false;
- if (!response.ok) { return response; }
-
- const label = this.change.labels[this.label];
- const labels = label.all || [];
- let wasChanged = false;
- for (let i = 0; i < labels.length; i++) {
- if (labels[i]._account_id === accountID) {
- for (const key in label) {
- if (label.hasOwnProperty(key) &&
- label[key]._account_id === accountID) {
- // Remove special label field, keeping change label values
- // in sync with the backend.
- this.change.labels[this.label][key] = null;
- }
- }
- this.change.labels[this.label].all.splice(i, 1);
- wasChanged = true;
- break;
- }
- }
- if (wasChanged) { this.notifySplices('change.labels'); }
+ if (!response.ok) { return; }
+ Gerrit.Nav.navigateToChange(this.change);
}).catch(err => {
target.disabled = false;
return;
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 2a1ad9e..d9b0cbf 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -169,6 +169,16 @@
delete(key) {
this._cache().delete(key);
}
+
+ invalidatePrefix(prefix) {
+ const newMap = new Map();
+ for (const [key, value] of this._cache().entries()) {
+ if (!key.startsWith(prefix)) {
+ newMap.set(key, value);
+ }
+ }
+ this._data.set(window.CANONICAL_PATH, newMap);
+ }
}
Polymer({
@@ -1207,6 +1217,20 @@
return this._sharedFetchPromises[req.url];
},
+ /**
+ * @param {string} prefix
+ */
+ _invalidateSharedFetchPromisesPrefix(prefix) {
+ const newObject = {};
+ Object.entries(this._sharedFetchPromises).forEach(([key, value]) => {
+ if (!key.startsWith(prefix)) {
+ newObject[key] = value;
+ }
+ });
+ this._sharedFetchPromises = newObject;
+ this._cache.invalidatePrefix(prefix);
+ },
+
_isNarrowScreen() {
return window.innerWidth < MAX_UNIFIED_DEFAULT_WINDOW_WIDTH_PX;
},
@@ -1297,21 +1321,27 @@
* @param {function()=} opt_cancelCondition
*/
getChangeDetail(changeNum, opt_errFn, opt_cancelCondition) {
- const options = this.listChangesOptionsToHex(
- this.ListChangesOption.ALL_COMMITS,
- this.ListChangesOption.ALL_REVISIONS,
- this.ListChangesOption.CHANGE_ACTIONS,
- this.ListChangesOption.CURRENT_ACTIONS,
- this.ListChangesOption.DETAILED_LABELS,
- this.ListChangesOption.DOWNLOAD_COMMANDS,
- this.ListChangesOption.MESSAGES,
- this.ListChangesOption.SUBMITTABLE,
- this.ListChangesOption.WEB_LINKS,
- this.ListChangesOption.SKIP_MERGEABLE
- );
- return this._getChangeDetail(
- changeNum, options, opt_errFn, opt_cancelCondition)
- .then(GrReviewerUpdatesParser.parse);
+ const options = [
+ this.ListChangesOption.ALL_COMMITS,
+ this.ListChangesOption.ALL_REVISIONS,
+ this.ListChangesOption.CHANGE_ACTIONS,
+ this.ListChangesOption.CURRENT_ACTIONS,
+ this.ListChangesOption.DETAILED_LABELS,
+ this.ListChangesOption.DOWNLOAD_COMMANDS,
+ this.ListChangesOption.MESSAGES,
+ this.ListChangesOption.SUBMITTABLE,
+ this.ListChangesOption.WEB_LINKS,
+ this.ListChangesOption.SKIP_MERGEABLE,
+ ];
+ return this.getConfig(false).then(config => {
+ if (config.receive && config.receive.enable_signed_push) {
+ options.push(this.ListChangesOption.PUSH_CERTIFICATES);
+ }
+ const optionsHex = this.listChangesOptionsToHex(...options);
+ return this._getChangeDetail(
+ changeNum, optionsHex, opt_errFn, opt_cancelCondition)
+ .then(GrReviewerUpdatesParser.parse);
+ });
},
/**
@@ -1527,25 +1557,20 @@
* @param {string} filter
* @param {number} groupsPerPage
* @param {number=} opt_offset
- * @return {!Promise<?Object>}
*/
- getGroups(filter, groupsPerPage, opt_offset) {
+ _getGroupsUrl(filter, groupsPerPage, opt_offset) {
const offset = opt_offset || 0;
- return this._fetchSharedCacheURL({
- url: `/groups/?n=${groupsPerPage + 1}&S=${offset}` +
- this._computeFilter(filter),
- anonymizedUrl: '/groups/?*',
- });
+ return `/groups/?n=${groupsPerPage + 1}&S=${offset}` +
+ this._computeFilter(filter);
},
/**
* @param {string} filter
* @param {number} reposPerPage
* @param {number=} opt_offset
- * @return {!Promise<?Object>}
*/
- getRepos(filter, reposPerPage, opt_offset) {
+ _getReposUrl(filter, reposPerPage, opt_offset) {
const defaultFilter = 'state:active OR state:read-only';
const namePartDelimiters = /[@.\-\s\/_]/g;
const offset = opt_offset || 0;
@@ -1572,11 +1597,46 @@
filter = filter.trim();
const encodedFilter = encodeURIComponent(filter);
+ return `/projects/?n=${reposPerPage + 1}&S=${offset}` +
+ `&query=${encodedFilter}`;
+ },
+
+ invalidateGroupsCache() {
+ this._invalidateSharedFetchPromisesPrefix('/groups/?');
+ },
+
+ invalidateReposCache(filter, reposPerPage, opt_offset) {
+ this._invalidateSharedFetchPromisesPrefix('/projects/?');
+ },
+
+ /**
+ * @param {string} filter
+ * @param {number} groupsPerPage
+ * @param {number=} opt_offset
+ * @return {!Promise<?Object>}
+ */
+ getGroups(filter, groupsPerPage, opt_offset) {
+ const url = this._getGroupsUrl(filter, groupsPerPage, opt_offset);
+
+ return this._fetchSharedCacheURL({
+ url,
+ anonymizedUrl: '/groups/?*',
+ });
+ },
+
+ /**
+ * @param {string} filter
+ * @param {number} reposPerPage
+ * @param {number=} opt_offset
+ * @return {!Promise<?Object>}
+ */
+ getRepos(filter, reposPerPage, opt_offset) {
+ const url = this._getReposUrl(filter, reposPerPage, opt_offset);
+
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
return this._fetchSharedCacheURL({
- url: `/projects/?n=${reposPerPage + 1}&S=${offset}` +
- `&query=${encodedFilter}`,
+ url,
anonymizedUrl: '/projects/?*',
});
},
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index eaac5ef..667f24c 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -96,6 +96,18 @@
});
});
+ test('cache invalidation', () => {
+ element._cache.set('/foo/bar', 1);
+ element._cache.set('/bar', 2);
+ element._sharedFetchPromises['/foo/bar'] = 3;
+ element._sharedFetchPromises['/bar'] = 4;
+ element._invalidateSharedFetchPromisesPrefix('/foo/');
+ assert.isFalse(element._cache.has('/foo/bar'));
+ assert.isTrue(element._cache.has('/bar'));
+ assert.isUndefined(element._sharedFetchPromises['/foo/bar']);
+ assert.strictEqual(4, element._sharedFetchPromises['/bar']);
+ });
+
test('params are properly encoded', () => {
let url = element._urlWithParams('/path/', {
sp: 'hola',
@@ -722,15 +734,6 @@
assert.deepEqual(element._send.lastCall.args[0].body, {token: 'foo'});
});
- test('GrReviewerUpdatesParser.parse is used', () => {
- sandbox.stub(GrReviewerUpdatesParser, 'parse').returns(
- Promise.resolve('foo'));
- return element.getChangeDetail(42).then(result => {
- assert.isTrue(GrReviewerUpdatesParser.parse.calledOnce);
- assert.equal(result, 'foo');
- });
- });
-
test('setAccountStatus', () => {
sandbox.stub(element, '_send').returns(Promise.resolve('OOO'));
element._cache.set('/accounts/self/detail', {});
@@ -935,6 +938,31 @@
});
});
+ test('normal use', () => {
+ const defaultQuery = 'state%3Aactive%20OR%20state%3Aread-only';
+
+ assert.equal(element._getReposUrl('test', 25),
+ '/projects/?n=26&S=0&query=test');
+
+ assert.equal(element._getReposUrl(null, 25),
+ `/projects/?n=26&S=0&query=${defaultQuery}`);
+
+ assert.equal(element._getReposUrl('test', 25, 25),
+ '/projects/?n=26&S=25&query=test');
+ });
+
+ test('invalidateReposCache', () => {
+ const url = '/projects/?n=26&S=0&query=test';
+
+ element._cache.set(url, {});
+
+ element.invalidateReposCache('test', 25);
+
+ assert.isUndefined(element._sharedFetchPromises[url]);
+
+ assert.isFalse(element._cache.has(url));
+ });
+
suite('getRepos', () => {
const defaultQuery = 'state%3Aactive%20OR%20state%3Aread-only';
@@ -999,11 +1027,57 @@
});
});
- test('getGroups filter regex', () => {
- sandbox.stub(element, '_fetchSharedCacheURL');
- element.getGroups('^test.*', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
- '/groups/?n=26&S=0&r=%5Etest.*');
+ test('_getGroupsUrl normal use', () => {
+ assert.equal(element._getGroupsUrl('test', 25),
+ '/groups/?n=26&S=0&m=test');
+
+ assert.equal(element._getGroupsUrl(null, 25),
+ '/groups/?n=26&S=0');
+
+ assert.equal(element._getGroupsUrl('test', 25, 25),
+ '/groups/?n=26&S=25&m=test');
+ });
+
+ test('invalidateGroupsCache', () => {
+ const url = '/groups/?n=26&S=0&m=test';
+
+ element._cache.set(url, {});
+
+ element.invalidateGroupsCache('test', 25);
+
+ assert.isUndefined(element._sharedFetchPromises[url]);
+
+ assert.isFalse(element._cache.has(url));
+ });
+
+ suite('getGroups', () => {
+ setup(() => {
+ sandbox.stub(element, '_fetchSharedCacheURL');
+ });
+
+ test('normal use', () => {
+ element.getGroups('test', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/groups/?n=26&S=0&m=test');
+
+ element.getGroups(null, 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/groups/?n=26&S=0');
+
+ element.getGroups('test', 25, 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/groups/?n=26&S=25&m=test');
+ });
+
+ test('regex', () => {
+ element.getGroups('^test.*', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/groups/?n=26&S=0&r=%5Etest.*');
+
+ element.getGroups('^test.*', 25, 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/groups/?n=26&S=25&r=%5Etest.*');
+ });
});
test('gerrit auth is used', () => {
@@ -1031,7 +1105,49 @@
});
});
- suite('_getChangeDetail', () => {
+ suite('getChangeDetail', () => {
+ suite('change detail options', () => {
+ let toHexStub;
+
+ setup(() => {
+ toHexStub = sandbox.stub(element, 'listChangesOptionsToHex',
+ options => 'deadbeef');
+ sandbox.stub(element, '_getChangeDetail',
+ async (changeNum, options) => ({changeNum, options}));
+ });
+
+ test('signed pushes disabled', async () => {
+ const {PUSH_CERTIFICATES} = element.ListChangesOption;
+ sandbox.stub(element, 'getConfig', async () => ({}));
+ const {changeNum, options} = await element.getChangeDetail(123);
+ assert.strictEqual(123, changeNum);
+ assert.strictEqual('deadbeef', options);
+ assert.isTrue(toHexStub.calledOnce);
+ assert.isFalse(toHexStub.lastCall.args.includes(PUSH_CERTIFICATES));
+ });
+
+ test('signed pushes enabled', async () => {
+ const {PUSH_CERTIFICATES} = element.ListChangesOption;
+ sandbox.stub(element, 'getConfig', async () => {
+ return {receive: {enable_signed_push: true}};
+ });
+ const {changeNum, options} = await element.getChangeDetail(123);
+ assert.strictEqual(123, changeNum);
+ assert.strictEqual('deadbeef', options);
+ assert.isTrue(toHexStub.calledOnce);
+ assert.isTrue(toHexStub.lastCall.args.includes(PUSH_CERTIFICATES));
+ });
+ });
+
+ test('GrReviewerUpdatesParser.parse is used', () => {
+ sandbox.stub(GrReviewerUpdatesParser, 'parse').returns(
+ Promise.resolve('foo'));
+ return element.getChangeDetail(42).then(result => {
+ assert.isTrue(GrReviewerUpdatesParser.parse.calledOnce);
+ assert.equal(result, 'foo');
+ });
+ });
+
test('_getChangeDetail passes params to ETags decorator', () => {
const changeNum = 4321;
element._projectLookup[changeNum] = 'test';