Merge "Move random Change-Id generation out of Change"
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index 00e5b97..41c241f 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -65,6 +65,18 @@
=== HTTP
+==== Jetty
+
+* `http/server/jetty/threadpool/active_threads`: Active threads
+* `http/server/jetty/threadpool/idle_threads`: Idle threads
+* `http/server/jetty/threadpool/reserved_threads`: Reserved threads
+* `http/server/jetty/threadpool/max_pool_size`: Maximum thread pool size
+* `http/server/jetty/threadpool/min_pool_size`: Minimum thread pool size
+* `http/server/jetty/threadpool/pool_size`: Current thread pool size
+* `http/server/jetty/threadpool/queue_size`: Queued requests waiting for a thread
+
+==== REST API
+
* `http/server/error_count`: Rate of REST API error responses.
* `http/server/success_count`: Rate of REST API success responses.
* `http/server/rest_api/count`: Rate of REST API calls by view.
diff --git a/java/com/google/gerrit/metrics/dropwizard/BUILD b/java/com/google/gerrit/metrics/dropwizard/BUILD
index 4b3859f..3079809 100644
--- a/java/com/google/gerrit/metrics/dropwizard/BUILD
+++ b/java/com/google/gerrit/metrics/dropwizard/BUILD
@@ -8,6 +8,7 @@
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/metrics",
+ "//java/com/google/gerrit/pgm/http/jetty",
"//java/com/google/gerrit/server",
"//lib:args4j",
"//lib:guava",
diff --git a/java/com/google/gerrit/pgm/http/jetty/BUILD b/java/com/google/gerrit/pgm/http/jetty/BUILD
index 7b1c3eb..32247fb 100644
--- a/java/com/google/gerrit/pgm/http/jetty/BUILD
+++ b/java/com/google/gerrit/pgm/http/jetty/BUILD
@@ -8,6 +8,7 @@
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/httpd",
"//java/com/google/gerrit/lifecycle",
+ "//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/sshd",
diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java b/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java
new file mode 100644
index 0000000..b6a2d38
--- /dev/null
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java
@@ -0,0 +1,91 @@
+// Copyright (C) 2020 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.pgm.http.jetty;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.metrics.CallbackMetric;
+import com.google.gerrit.metrics.CallbackMetric0;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class JettyMetrics {
+
+ @Inject
+ JettyMetrics(JettyServer jetty, MetricMaker metrics) {
+ CallbackMetric0<Integer> minPoolSize =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/min_pool_size",
+ Integer.class,
+ new Description("Minimum thread pool size").setGauge());
+ CallbackMetric0<Integer> maxPoolSize =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/max_pool_size",
+ Integer.class,
+ new Description("Maximum thread pool size").setGauge());
+ CallbackMetric0<Integer> poolSize =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/pool_size",
+ Integer.class,
+ new Description("Current thread pool size").setGauge());
+ CallbackMetric0<Integer> idleThreads =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/idle_threads",
+ Integer.class,
+ new Description("Idle threads").setGauge().setUnit("threads"));
+ CallbackMetric0<Integer> busyThreads =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/active_threads",
+ Integer.class,
+ new Description("Active threads").setGauge().setUnit("threads"));
+ CallbackMetric0<Integer> reservedThreads =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/reserved_threads",
+ Integer.class,
+ new Description("Reserved threads").setGauge().setUnit("threads"));
+ CallbackMetric0<Integer> queueSize =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/queue_size",
+ Integer.class,
+ new Description("Queued requests waiting for a thread").setGauge().setUnit("requests"));
+ CallbackMetric0<Boolean> lowOnThreads =
+ metrics.newCallbackMetric(
+ "http/server/jetty/threadpool/is_low_on_threads",
+ Boolean.class,
+ new Description("Whether thread pool is low on threads").setGauge());
+ JettyServer.Metrics jettyMetrics = jetty.getMetrics();
+ metrics.newTrigger(
+ ImmutableSet.<CallbackMetric<?>>of(
+ idleThreads,
+ busyThreads,
+ reservedThreads,
+ minPoolSize,
+ maxPoolSize,
+ poolSize,
+ queueSize,
+ lowOnThreads),
+ () -> {
+ minPoolSize.set(jettyMetrics.getMinThreads());
+ maxPoolSize.set(jettyMetrics.getMaxThreads());
+ poolSize.set(jettyMetrics.getThreads());
+ idleThreads.set(jettyMetrics.getIdleThreads());
+ busyThreads.set(jettyMetrics.getBusyThreads());
+ reservedThreads.set(jettyMetrics.getReservedThreads());
+ queueSize.set(jettyMetrics.getQueueSize());
+ lowOnThreads.set(jettyMetrics.isLowOnThreads());
+ });
+ }
+}
diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyModule.java b/java/com/google/gerrit/pgm/http/jetty/JettyModule.java
index c818276..32a8b6d 100644
--- a/java/com/google/gerrit/pgm/http/jetty/JettyModule.java
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyModule.java
@@ -31,5 +31,6 @@
bind(JettyServer.class);
listener().to(JettyServer.Lifecycle.class);
install(new FactoryModuleBuilder().build(HttpLogFactory.class));
+ bind(JettyMetrics.class);
}
}
diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
index 096e4a1..5851fd0 100644
--- a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -68,7 +68,6 @@
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jgit.lib.Config;
@Singleton
@@ -117,9 +116,49 @@
}
}
+ static class Metrics {
+ private final QueuedThreadPool threadPool;
+
+ Metrics(QueuedThreadPool threadPool) {
+ this.threadPool = threadPool;
+ }
+
+ public int getIdleThreads() {
+ return threadPool.getIdleThreads();
+ }
+
+ public int getBusyThreads() {
+ return threadPool.getBusyThreads();
+ }
+
+ public int getReservedThreads() {
+ return threadPool.getReservedThreads();
+ }
+
+ public int getMinThreads() {
+ return threadPool.getMinThreads();
+ }
+
+ public int getMaxThreads() {
+ return threadPool.getMaxThreads();
+ }
+
+ public int getThreads() {
+ return threadPool.getThreads();
+ }
+
+ public int getQueueSize() {
+ return threadPool.getQueueSize();
+ }
+
+ public boolean isLowOnThreads() {
+ return threadPool.isLowOnThreads();
+ }
+ }
+
private final SitePaths site;
private final Server httpd;
-
+ private final Metrics metrics;
private boolean reverseProxy;
@Inject
@@ -131,8 +170,10 @@
HttpLogFactory httpLogFactory) {
this.site = site;
- httpd = new Server(threadPool(cfg, threadSettingsConfig));
+ QueuedThreadPool pool = threadPool(cfg, threadSettingsConfig);
+ httpd = new Server(pool);
httpd.setConnectors(listen(httpd, cfg));
+ metrics = new Metrics(pool);
Handler app = makeContext(env, cfg);
if (cfg.getBoolean("httpd", "requestLog", !reverseProxy)) {
@@ -161,6 +202,10 @@
httpd.setStopAtShutdown(false);
}
+ Metrics getMetrics() {
+ return metrics;
+ }
+
private Connector[] listen(Server server, Config cfg) {
// OpenID and certain web-based single-sign-on products can cause
// some very long headers, especially in the Referer header. We
@@ -336,7 +381,7 @@
return site.resolve(path);
}
- private ThreadPool threadPool(Config cfg, ThreadSettingsConfig threadSettingsConfig) {
+ private QueuedThreadPool threadPool(Config cfg, ThreadSettingsConfig threadSettingsConfig) {
int maxThreads = threadSettingsConfig.getHttpdMaxThreads();
int minThreads = cfg.getInt("httpd", null, "minthreads", 5);
int maxQueued = cfg.getInt("httpd", null, "maxqueued", 200);
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index d07ef0c..c53ba83 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -337,7 +337,7 @@
@Override
public boolean accountBelongsToRealm(Collection<ExternalId> externalIds) {
for (ExternalId id : externalIds) {
- if (id.toString().contains(SCHEME_GERRIT)) {
+ if (id.isScheme(SCHEME_GERRIT)) {
return true;
}
}
diff --git a/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java b/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java
index 1a50014..944bd44 100644
--- a/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java
+++ b/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java
@@ -122,7 +122,7 @@
@Override
public boolean accountBelongsToRealm(Collection<ExternalId> externalIds) {
for (ExternalId id : externalIds) {
- if (id.toString().contains(SCHEME_EXTERNAL)) {
+ if (id.isScheme(SCHEME_EXTERNAL)) {
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
index 7a03005..f690acb 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
@@ -16,6 +16,7 @@
import static java.util.stream.Collectors.toSet;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -79,12 +80,14 @@
public Response<?> apply(IdentifiedUser user, String email)
throws ResourceNotFoundException, ResourceConflictException, MethodNotAllowedException,
IOException, ConfigInvalidException {
- if (!realm.allowsEdit(AccountFieldName.REGISTER_NEW_EMAIL)) {
+ Account.Id accountId = user.getAccountId();
+ if (realm.accountBelongsToRealm(externalIds.byAccount(accountId))
+ && !realm.allowsEdit(AccountFieldName.REGISTER_NEW_EMAIL)) {
throw new MethodNotAllowedException("realm does not allow deleting emails");
}
Set<ExternalId> extIds =
- externalIds.byAccount(user.getAccountId()).stream()
+ externalIds.byAccount(accountId).stream()
.filter(e -> email.equals(e.email()))
.collect(toSet());
if (extIds.isEmpty()) {
diff --git a/java/com/google/gerrit/server/restapi/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java
index c7496b9..78430c3 100644
--- a/java/com/google/gerrit/server/restapi/account/PutName.java
+++ b/java/com/google/gerrit/server/restapi/account/PutName.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.common.NameInput;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -29,6 +30,7 @@
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -50,6 +52,7 @@
private final Provider<CurrentUser> self;
private final Realm realm;
private final PermissionBackend permissionBackend;
+ private final ExternalIds externalIds;
private final Provider<AccountsUpdate> accountsUpdateProvider;
@Inject
@@ -57,10 +60,12 @@
Provider<CurrentUser> self,
Realm realm,
PermissionBackend permissionBackend,
+ ExternalIds externalIds,
@ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider) {
this.self = self;
this.realm = realm;
this.permissionBackend = permissionBackend;
+ this.externalIds = externalIds;
this.accountsUpdateProvider = accountsUpdateProvider;
}
@@ -81,7 +86,9 @@
input = new NameInput();
}
- if (!realm.allowsEdit(AccountFieldName.FULL_NAME)) {
+ Account.Id accountId = user.getAccountId();
+ if (realm.accountBelongsToRealm(externalIds.byAccount(accountId))
+ && !realm.allowsEdit(AccountFieldName.FULL_NAME)) {
throw new MethodNotAllowedException("realm does not allow editing name");
}
@@ -89,7 +96,7 @@
AccountState accountState =
accountsUpdateProvider
.get()
- .update("Set Full Name via API", user.getAccountId(), u -> u.setFullName(newName))
+ .update("Set Full Name via API", accountId, u -> u.setFullName(newName))
.orElseThrow(() -> new ResourceNotFoundException("account not found"));
return Strings.isNullOrEmpty(accountState.account().fullName())
? Response.none()
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index dc841b8..05bf1fd 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -89,15 +89,16 @@
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
- if (!realm.allowsEdit(AccountFieldName.USER_NAME)) {
- throw new MethodNotAllowedException("realm does not allow editing username");
- }
-
Account.Id accountId = rsrc.getUser().getAccountId();
if (!externalIds.byAccount(accountId, SCHEME_USERNAME).isEmpty()) {
throw new MethodNotAllowedException("Username cannot be changed.");
}
+ if (realm.accountBelongsToRealm(externalIds.byAccount(accountId))
+ && !realm.allowsEdit(AccountFieldName.USER_NAME)) {
+ throw new MethodNotAllowedException("realm does not allow editing username");
+ }
+
if (input == null || Strings.isNullOrEmpty(input.username)) {
throw new BadRequestException("input required");
}
diff --git a/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
new file mode 100644
index 0000000..ba40d8c
--- /dev/null
+++ b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
@@ -0,0 +1,94 @@
+// Copyright (C) 2020 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.auth.ldap;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_CACHE;
+import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_EXIST_CACHE;
+import static com.google.gerrit.server.auth.ldap.LdapModule.PARENT_GROUPS_CACHE;
+import static com.google.gerrit.server.auth.ldap.LdapModule.USERNAME_CACHE;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.testing.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+
+public final class LdapRealmTest {
+ @Inject private LdapRealm ldapRealm = null;
+
+ @Before
+ public void setUpInjector() throws Exception {
+ Injector injector =
+ Guice.createInjector(
+ new InMemoryModule(),
+ new CacheModule() {
+ @Override
+ protected void configure() {
+ cache(GROUP_CACHE, String.class, new TypeLiteral<Set<AccountGroup.UUID>>() {})
+ .loader(LdapRealm.MemberLoader.class);
+ cache(USERNAME_CACHE, String.class, new TypeLiteral<Optional<Account.Id>>() {})
+ .loader(LdapRealm.UserLoader.class);
+ cache(GROUP_EXIST_CACHE, String.class, new TypeLiteral<Boolean>() {})
+ .loader(LdapRealm.ExistenceLoader.class);
+ cache(
+ PARENT_GROUPS_CACHE, String.class, new TypeLiteral<ImmutableSet<String>>() {});
+ }
+ });
+ injector.injectMembers(this);
+ }
+
+ private ExternalId id(String scheme, String id) {
+ return ExternalId.create(scheme, id, Account.id(1000));
+ }
+
+ private boolean accountBelongsToRealm(ExternalId... ids) {
+ return ldapRealm.accountBelongsToRealm(Arrays.asList(ids));
+ }
+
+ private boolean accountBelongsToRealm(String scheme, String id) {
+ return accountBelongsToRealm(id(scheme, id));
+ }
+
+ @Test
+ public void accountBelongsToRealm() throws Exception {
+ assertThat(accountBelongsToRealm(SCHEME_GERRIT, "test")).isTrue();
+ assertThat(accountBelongsToRealm(id(SCHEME_USERNAME, "test"), id(SCHEME_GERRIT, "test")))
+ .isTrue();
+ assertThat(accountBelongsToRealm(id(SCHEME_GERRIT, "test"), id(SCHEME_USERNAME, "test")))
+ .isTrue();
+
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "foo@bar.com")).isFalse();
+
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "gerrit")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "xxgerritxx")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "gerrit.foo@bar.com")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "bar.gerrit@bar.com")).isFalse();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
new file mode 100644
index 0000000..dc62a61
--- /dev/null
+++ b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
@@ -0,0 +1,69 @@
+// Copyright (C) 2020 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.auth.oauth;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXTERNAL;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.testing.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+
+public final class OAuthRealmTest {
+ @Inject private OAuthRealm oauthRealm = null;
+
+ @Before
+ public void setUpInjector() throws Exception {
+ Injector injector = Guice.createInjector(new InMemoryModule());
+ injector.injectMembers(this);
+ }
+
+ private ExternalId id(String scheme, String id) {
+ return ExternalId.create(scheme, id, Account.id(1000));
+ }
+
+ private boolean accountBelongsToRealm(ExternalId... ids) {
+ return oauthRealm.accountBelongsToRealm(Arrays.asList(ids));
+ }
+
+ private boolean accountBelongsToRealm(String scheme, String id) {
+ return accountBelongsToRealm(id(scheme, id));
+ }
+
+ @Test
+ public void accountBelongsToRealm() throws Exception {
+ assertThat(accountBelongsToRealm(SCHEME_EXTERNAL, "test")).isTrue();
+ assertThat(accountBelongsToRealm(id(SCHEME_USERNAME, "test"), id(SCHEME_EXTERNAL, "test")))
+ .isTrue();
+ assertThat(accountBelongsToRealm(id(SCHEME_EXTERNAL, "test"), id(SCHEME_USERNAME, "test")))
+ .isTrue();
+
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "foo@bar.com")).isFalse();
+
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "external")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "xxexternalxx")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "external.foo@bar.com")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "bar.external@bar.com")).isFalse();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java b/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
index bfcb9e4..3a8d7e4 100644
--- a/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
+++ b/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
@@ -176,13 +176,14 @@
private static Ref createRefWithNonEmptyTreeCommit(Repository usersRepo, int changeId, int userId)
throws IOException {
- RevWalk rw = new RevWalk(usersRepo);
- ObjectId fileObj = createBlob(usersRepo, String.format("file %d content", changeId));
- ObjectId treeObj =
- createTree(usersRepo, rw.lookupBlob(fileObj), String.format("file%d.txt", changeId));
- ObjectId commitObj = createCommit(usersRepo, treeObj, null);
- Ref refObj = createRef(usersRepo, commitObj, getRefName(changeId, userId));
- return refObj;
+ try (RevWalk rw = new RevWalk(usersRepo)) {
+ ObjectId fileObj = createBlob(usersRepo, String.format("file %d content", changeId));
+ ObjectId treeObj =
+ createTree(usersRepo, rw.lookupBlob(fileObj), String.format("file%d.txt", changeId));
+ ObjectId commitObj = createCommit(usersRepo, treeObj, null);
+ Ref refObj = createRef(usersRepo, commitObj, getRefName(changeId, userId));
+ return refObj;
+ }
}
private static Ref createRefWithEmptyTreeCommit(Repository usersRepo, int changeId, int userId)
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index dea9789..ff52ddd 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -176,7 +176,7 @@
.ignoredWhitespaceOnly .content.remove .contentText .intraline,
.delta.total.ignoredWhitespaceOnly .content.remove .contentText,
.ignoredWhitespaceOnly .content.remove .contentText {
- background: none;
+ background-color: var(--view-background-color);
}
.content .contentText:empty:after {
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index e9477bf..68b6756 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -50,7 +50,11 @@
element.config = {
ph: {
match: '([Bb]ug|[Ii]ssue)\\s*#?(\\d+)',
- link: 'https://code.google.com/p/gerrit/issues/detail?id=$2',
+ link: 'https://bugs.chromium.org/p/gerrit/issues/detail?id=$2',
+ },
+ prefixsameinlinkandpattern: {
+ match: '([Hh][Tt][Tt][Pp]example)\\s*#?(\\d+)',
+ link: 'https://bugs.chromium.org/p/gerrit/issues/detail?id=$2',
},
changeid: {
match: '(I[0-9a-f]{8,40})',
@@ -91,7 +95,7 @@
test('URL pattern was parsed and linked.', () => {
// Regular inline link.
- const url = 'https://code.google.com/p/gerrit/issues/detail?id=3650';
+ const url = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
element.content = url;
const linkEl = element.$.output.childNodes[0];
assert.equal(linkEl.target, '_blank');
@@ -105,7 +109,7 @@
element.content = 'Issue 3650';
let linkEl = element.$.output.childNodes[0];
- const url = 'https://code.google.com/p/gerrit/issues/detail?id=3650';
+ const url = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
assert.equal(linkEl.target, '_blank');
assert.equal(linkEl.href, url);
assert.equal(linkEl.textContent, 'Issue 3650');
@@ -118,6 +122,18 @@
assert.equal(linkEl.textContent, 'Bug 3650');
});
+ test('Pattern with same prefix as link was correctly parsed', () => {
+ // Pattern starts with the same prefix (`http`) as the url.
+ element.content = 'httpexample 3650';
+
+ assert.equal(element.$.output.childNodes.length, 1);
+ const linkEl = element.$.output.childNodes[0];
+ const url = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
+ assert.equal(linkEl.target, '_blank');
+ assert.equal(linkEl.href, url);
+ assert.equal(linkEl.textContent, 'httpexample 3650');
+ });
+
test('Change-Id pattern was parsed and linked', () => {
// "Change-Id:" pattern.
const changeID = 'I11d6a37f5e9b5df0486f6c922d8836dfa780e03e';
@@ -159,12 +175,12 @@
assert.equal(linkEl1.target, '_blank');
assert.equal(linkEl1.href,
- 'https://code.google.com/p/gerrit/issues/detail?id=3650');
+ 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650');
assert.equal(linkEl1.textContent, 'Issue 3650');
assert.equal(linkEl2.target, '_blank');
assert.equal(linkEl2.href,
- 'https://code.google.com/p/gerrit/issues/detail?id=3450');
+ 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3450');
assert.equal(linkEl2.textContent, 'Issue 3450');
});
@@ -177,7 +193,7 @@
const bug = 'Issue 3650';
const changeUrl = '/q/' + changeID;
- const bugUrl = 'https://code.google.com/p/gerrit/issues/detail?id=3650';
+ const bugUrl = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
element.content = prefix + changeID + bug;
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
index eb2b0d0..c4f427e 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
@@ -304,14 +304,15 @@
let result = match[0].replace(pattern,
patterns[p].html || patterns[p].link);
- let i;
- // Skip portion of replacement string that is equal to original.
- for (i = 0; i < result.length; i++) {
- if (result[i] !== match[0][i]) { break; }
- }
- result = result.slice(i);
-
if (patterns[p].html) {
+ let i;
+ // Skip portion of replacement string that is equal to original to
+ // allow overlapping patterns.
+ for (i = 0; i < result.length; i++) {
+ if (result[i] !== match[0][i]) { break; }
+ }
+ result = result.slice(i);
+
this.addHTML(
result,
susbtrIndex + match.index + i,
@@ -321,8 +322,8 @@
this.addLink(
match[0],
result,
- susbtrIndex + match.index + i,
- match[0].length - i,
+ susbtrIndex + match.index,
+ match[0].length,
outputArray);
} else {
throw Error('linkconfig entry ' + p +
diff --git a/tools/js/download_bower.py b/tools/js/download_bower.py
index c9a5df6..c1d7d00 100755
--- a/tools/js/download_bower.py
+++ b/tools/js/download_bower.py
@@ -46,7 +46,8 @@
raise
out, err = p.communicate()
if p.returncode:
- sys.stderr.write(err)
+ # For python3 support we wrap str around err.
+ sys.stderr.write(str(err))
raise OSError('Command failed: %s' % ' '.join(cmd))
try: