Merge changes I2f3f5e0c,If9522629
* changes:
Change test API: Allow to specify author/committer for new patch sets
Change test API: Allow to specify author/committer for new changes
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index df2c5cb..7293f35 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -46,6 +46,7 @@
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
import com.google.gerrit.pgm.util.LogFileCompressor.LogFileCompressorModule;
+import com.google.gerrit.server.DefaultRefLogIdentityProvider;
import com.google.gerrit.server.LibModuleLoader;
import com.google.gerrit.server.LibModuleType;
import com.google.gerrit.server.ModuleOverloader;
@@ -310,6 +311,7 @@
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
modules.add(new GerritApiModule());
modules.add(new ProjectQueryBuilderModule());
+ modules.add(new DefaultRefLogIdentityProvider.Module());
modules.add(new PluginApiModule());
modules.add(new SearchingChangeCacheImplModule());
modules.add(new InternalAccountDirectoryModule());
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 0342fe5..845cc9a 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -55,6 +55,7 @@
import com.google.gerrit.pgm.util.LogFileCompressor.LogFileCompressorModule;
import com.google.gerrit.pgm.util.RuntimeShutdown;
import com.google.gerrit.pgm.util.SiteProgram;
+import com.google.gerrit.server.DefaultRefLogIdentityProvider;
import com.google.gerrit.server.LibModuleLoader;
import com.google.gerrit.server.LibModuleType;
import com.google.gerrit.server.ModuleOverloader;
@@ -448,6 +449,7 @@
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
modules.add(new GerritApiModule());
modules.add(new ProjectQueryBuilderModule());
+ modules.add(new DefaultRefLogIdentityProvider.Module());
modules.add(new PluginApiModule());
modules.add(new SearchingChangeCacheImplModule(replica));
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 5bffce7..cae7ca6 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -28,6 +28,7 @@
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.DefaultRefLogIdentityProvider;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.LibModuleLoader;
@@ -83,19 +84,19 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRequirementsEvaluatorImpl;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
-import com.google.gerrit.server.query.FileEditsPredicate;
import com.google.gerrit.server.query.approval.ApprovalModule;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ConflictsCacheImpl;
-import com.google.gerrit.server.query.change.DistinctVotersPredicate;
-import com.google.gerrit.server.query.change.HasSubmoduleUpdatePredicate;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule.DefaultSubmitRuleModule;
import com.google.gerrit.server.rules.IgnoreSelfApprovalRule.IgnoreSelfApprovalRuleModule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.SubmitRule;
+import com.google.gerrit.server.submitrequirement.predicate.DistinctVotersPredicate;
+import com.google.gerrit.server.submitrequirement.predicate.FileEditsPredicate;
+import com.google.gerrit.server.submitrequirement.predicate.HasSubmoduleUpdatePredicate;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.inject.Injector;
import com.google.inject.Key;
@@ -127,6 +128,7 @@
modules.add(PatchListCacheImpl.module());
modules.add(new DefaultUrlFormatterModule());
modules.add(DiffOperationsImpl.module());
+ modules.add(new DefaultRefLogIdentityProvider.Module());
// There is the concept of LifecycleModule, in Gerrit's own extension to Guice, which has these:
// listener().to(SomeClassImplementingLifecycleListener.class);
diff --git a/java/com/google/gerrit/server/DefaultRefLogIdentityProvider.java b/java/com/google/gerrit/server/DefaultRefLogIdentityProvider.java
new file mode 100644
index 0000000..bef276a
--- /dev/null
+++ b/java/com/google/gerrit/server/DefaultRefLogIdentityProvider.java
@@ -0,0 +1,97 @@
+// Copyright (C) 2023 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;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.server.config.AnonymousCowardName;
+import com.google.gerrit.server.config.EnablePeerIPInReflogRecord;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.time.Instant;
+import java.time.ZoneId;
+import org.eclipse.jgit.lib.PersonIdent;
+
+@Singleton
+public class DefaultRefLogIdentityProvider implements RefLogIdentityProvider {
+ public static class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(RefLogIdentityProvider.class).to(DefaultRefLogIdentityProvider.class);
+ }
+ }
+
+ private final String anonymousCowardName;
+ private final Boolean enablePeerIPInReflogRecord;
+
+ @Inject
+ DefaultRefLogIdentityProvider(
+ @AnonymousCowardName String anonymousCowardName,
+ @EnablePeerIPInReflogRecord Boolean enablePeerIPInReflogRecord) {
+ this.anonymousCowardName = anonymousCowardName;
+ this.enablePeerIPInReflogRecord = enablePeerIPInReflogRecord;
+ }
+
+ @Override
+ public PersonIdent newRefLogIdent(IdentifiedUser user, Instant when, ZoneId zoneId) {
+ Account account = user.getAccount();
+
+ String name = account.fullName();
+ if (name == null || name.isEmpty()) {
+ name = account.preferredEmail();
+ }
+ if (name == null || name.isEmpty()) {
+ name = anonymousCowardName;
+ }
+
+ String email;
+ if (enablePeerIPInReflogRecord) {
+ email = constructMailAddress(user, guessHost(user));
+ } else {
+ email =
+ Strings.isNullOrEmpty(account.preferredEmail())
+ ? constructMailAddress(user, "unknown")
+ : account.preferredEmail();
+ }
+
+ return new PersonIdent(name, email, when, zoneId);
+ }
+
+ private String constructMailAddress(IdentifiedUser user, String host) {
+ return user.getUserName().orElse("")
+ + "|account-"
+ + user.getAccountId().toString()
+ + "@"
+ + host;
+ }
+
+ private String guessHost(IdentifiedUser user) {
+ String host = null;
+ SocketAddress remotePeer = user.getRemotePeer();
+ if (remotePeer instanceof InetSocketAddress) {
+ InetSocketAddress sa = (InetSocketAddress) remotePeer;
+ InetAddress in = sa.getAddress();
+ host = in != null ? in.getHostAddress() : sa.getHostName();
+ }
+ if (Strings.isNullOrEmpty(host)) {
+ return "unknown";
+ }
+ return host;
+ }
+}
diff --git a/java/com/google/gerrit/server/IdentifiedUser.java b/java/com/google/gerrit/server/IdentifiedUser.java
index eda6e09..36d7888 100644
--- a/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/java/com/google/gerrit/server/IdentifiedUser.java
@@ -19,7 +19,6 @@
import static com.google.common.flogger.LazyArgs.lazy;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
@@ -44,8 +43,6 @@
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
import com.google.inject.util.Providers;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.SocketAddress;
import java.net.URL;
@@ -66,6 +63,7 @@
private final AuthConfig authConfig;
private final Realm realm;
private final String anonymousCowardName;
+ private final RefLogIdentityProvider refLogIdentityProvider;
private final Provider<String> canonicalUrl;
private final AccountCache accountCache;
private final GroupBackend groupBackend;
@@ -76,6 +74,7 @@
AuthConfig authConfig,
Realm realm,
@AnonymousCowardName String anonymousCowardName,
+ RefLogIdentityProvider refLogIdentityProvider,
@CanonicalWebUrl Provider<String> canonicalUrl,
@EnablePeerIPInReflogRecord Boolean enablePeerIPInReflogRecord,
AccountCache accountCache,
@@ -83,6 +82,7 @@
this.authConfig = authConfig;
this.realm = realm;
this.anonymousCowardName = anonymousCowardName;
+ this.refLogIdentityProvider = refLogIdentityProvider;
this.canonicalUrl = canonicalUrl;
this.accountCache = accountCache;
this.groupBackend = groupBackend;
@@ -94,6 +94,7 @@
authConfig,
realm,
anonymousCowardName,
+ refLogIdentityProvider,
canonicalUrl,
accountCache,
groupBackend,
@@ -131,6 +132,7 @@
authConfig,
realm,
anonymousCowardName,
+ refLogIdentityProvider,
canonicalUrl,
accountCache,
groupBackend,
@@ -153,6 +155,7 @@
private final AuthConfig authConfig;
private final Realm realm;
private final String anonymousCowardName;
+ private final RefLogIdentityProvider refLogIdentityProvider;
private final Provider<String> canonicalUrl;
private final AccountCache accountCache;
private final GroupBackend groupBackend;
@@ -164,6 +167,7 @@
AuthConfig authConfig,
Realm realm,
@AnonymousCowardName String anonymousCowardName,
+ RefLogIdentityProvider refLogIdentityProvider,
@CanonicalWebUrl Provider<String> canonicalUrl,
AccountCache accountCache,
GroupBackend groupBackend,
@@ -172,6 +176,7 @@
this.authConfig = authConfig;
this.realm = realm;
this.anonymousCowardName = anonymousCowardName;
+ this.refLogIdentityProvider = refLogIdentityProvider;
this.canonicalUrl = canonicalUrl;
this.accountCache = accountCache;
this.groupBackend = groupBackend;
@@ -188,6 +193,7 @@
authConfig,
realm,
anonymousCowardName,
+ refLogIdentityProvider,
canonicalUrl,
accountCache,
groupBackend,
@@ -203,6 +209,7 @@
authConfig,
realm,
anonymousCowardName,
+ refLogIdentityProvider,
canonicalUrl,
accountCache,
groupBackend,
@@ -224,6 +231,7 @@
private final Realm realm;
private final GroupBackend groupBackend;
private final String anonymousCowardName;
+ private final RefLogIdentityProvider refLogIdentityProvider;
private final Boolean enablePeerIPInReflogRecord;
private final Set<String> validEmails = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
private final CurrentUser realUser; // Must be final since cached properties depend on it.
@@ -235,11 +243,13 @@
private boolean loadedAllEmails;
private Set<String> invalidEmails;
private GroupMembership effectiveGroups;
+ private PersonIdent refLogIdent;
private IdentifiedUser(
AuthConfig authConfig,
Realm realm,
String anonymousCowardName,
+ RefLogIdentityProvider refLogIdentityProvider,
Provider<String> canonicalUrl,
AccountCache accountCache,
GroupBackend groupBackend,
@@ -251,6 +261,7 @@
authConfig,
realm,
anonymousCowardName,
+ refLogIdentityProvider,
canonicalUrl,
accountCache,
groupBackend,
@@ -266,6 +277,7 @@
AuthConfig authConfig,
Realm realm,
String anonymousCowardName,
+ RefLogIdentityProvider refLogIdentityProvider,
Provider<String> canonicalUrl,
AccountCache accountCache,
GroupBackend groupBackend,
@@ -281,6 +293,7 @@
this.authConfig = authConfig;
this.realm = realm;
this.anonymousCowardName = anonymousCowardName;
+ this.refLogIdentityProvider = refLogIdentityProvider;
this.enablePeerIPInReflogRecord = enablePeerIPInReflogRecord;
this.remotePeerProvider = remotePeerProvider;
this.accountId = id;
@@ -426,36 +439,27 @@
return getAccountId();
}
+ @Nullable
+ public SocketAddress getRemotePeer() {
+ try {
+ return remotePeerProvider.get();
+ } catch (OutOfScopeException | ProvisionException e) {
+ return null;
+ }
+ }
+
public PersonIdent newRefLogIdent() {
- return newRefLogIdent(Instant.now(), ZoneId.systemDefault());
+ return refLogIdentityProvider.newRefLogIdent(this);
}
public PersonIdent newRefLogIdent(Instant when, ZoneId zoneId) {
- final Account ua = getAccount();
-
- String name = ua.fullName();
- if (name == null || name.isEmpty()) {
- name = ua.preferredEmail();
+ if (refLogIdent != null) {
+ refLogIdent =
+ new PersonIdent(refLogIdent.getName(), refLogIdent.getEmailAddress(), when, zoneId);
+ return refLogIdent;
}
- if (name == null || name.isEmpty()) {
- name = anonymousCowardName;
- }
-
- String user;
- if (enablePeerIPInReflogRecord) {
- user = constructMailAddress(ua, guessHost());
- } else {
- user =
- Strings.isNullOrEmpty(ua.preferredEmail())
- ? constructMailAddress(ua, "unknown")
- : ua.preferredEmail();
- }
-
- return new PersonIdent(name, user, when, zoneId);
- }
-
- private String constructMailAddress(Account ua, String host) {
- return getUserName().orElse("") + "|account-" + ua.id().toString() + "@" + host;
+ refLogIdent = refLogIdentityProvider.newRefLogIdent(this, when, zoneId);
+ return refLogIdent;
}
public PersonIdent newCommitterIdent(PersonIdent ident) {
@@ -533,6 +537,7 @@
authConfig,
realm,
anonymousCowardName,
+ refLogIdentityProvider,
Providers.of(canonicalUrl.get()),
accountCache,
groupBackend,
@@ -546,23 +551,4 @@
public boolean hasSameAccountId(CurrentUser other) {
return getAccountId().get() == other.getAccountId().get();
}
-
- private String guessHost() {
- String host = null;
- SocketAddress remotePeer = null;
- try {
- remotePeer = remotePeerProvider.get();
- } catch (OutOfScopeException | ProvisionException e) {
- // Leave null.
- }
- if (remotePeer instanceof InetSocketAddress) {
- InetSocketAddress sa = (InetSocketAddress) remotePeer;
- InetAddress in = sa.getAddress();
- host = in != null ? in.getHostAddress() : sa.getHostName();
- }
- if (Strings.isNullOrEmpty(host)) {
- return "unknown";
- }
- return host;
- }
}
diff --git a/java/com/google/gerrit/server/RefLogIdentityProvider.java b/java/com/google/gerrit/server/RefLogIdentityProvider.java
new file mode 100644
index 0000000..2a5d2b0
--- /dev/null
+++ b/java/com/google/gerrit/server/RefLogIdentityProvider.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 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;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import org.eclipse.jgit.lib.PersonIdent;
+
+/**
+ * Extension point that allows to control which identity should be recorded in the reflog for ref
+ * updates done by a user or done on behalf of a user.
+ */
+public interface RefLogIdentityProvider {
+ /**
+ * Creates a {@link PersonIdent} for the given user that should be used as the user identity in
+ * the reflog for ref updates done by this user or done on behalf of this user.
+ *
+ * <p>The returned {@link PersonIdent} is created with the current timestamp and the system
+ * default timezone.
+ *
+ * @param user the user for which a reflog identity should be created
+ */
+ default PersonIdent newRefLogIdent(IdentifiedUser user) {
+ return newRefLogIdent(user, Instant.now(), ZoneId.systemDefault());
+ }
+
+ /**
+ * Creates a {@link PersonIdent} for the given user that should be used as the user identity in
+ * the reflog for ref updates done by this user or done on behalf of this user.
+ *
+ * @param user the user for which a reflog identity should be created
+ * @param when the timestamp that should be used to create the {@link PersonIdent}
+ * @param zoneId the zone ID identifying the timezone that should be used to create the {@link
+ * PersonIdent}
+ */
+ PersonIdent newRefLogIdent(IdentifiedUser user, Instant when, ZoneId zoneId);
+}
diff --git a/java/com/google/gerrit/server/comment/CommentContextLoader.java b/java/com/google/gerrit/server/comment/CommentContextLoader.java
index 8fbb259..0df7729 100644
--- a/java/com/google/gerrit/server/comment/CommentContextLoader.java
+++ b/java/com/google/gerrit/server/comment/CommentContextLoader.java
@@ -225,6 +225,11 @@
private static Optional<Range> getStartAndEndLines(ContextInput comment) {
if (comment.range() != null) {
+ if (comment.range().endLine < comment.range().startLine) {
+ // Seems like comments, created in reply to robot comments sometimes have invalid ranges
+ // Fix here, otherwise the range is invalid and we throw an error later on.
+ return Optional.of(Range.create(comment.range().startLine, comment.range().startLine + 1));
+ }
return Optional.of(Range.create(comment.range().startLine, comment.range().endLine + 1));
} else if (comment.lineNumber() > 0) {
return Optional.of(Range.create(comment.lineNumber(), comment.lineNumber() + 1));
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index f442500..f194434 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -194,14 +194,11 @@
import com.google.gerrit.server.project.SubmitRequirementConfigValidator;
import com.google.gerrit.server.project.SubmitRequirementsEvaluatorImpl;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
-import com.google.gerrit.server.query.FileEditsPredicate;
import com.google.gerrit.server.query.approval.ApprovalModule;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ConflictsCacheImpl;
-import com.google.gerrit.server.query.change.DistinctVotersPredicate;
-import com.google.gerrit.server.query.change.HasSubmoduleUpdatePredicate;
import com.google.gerrit.server.quota.QuotaEnforcer;
import com.google.gerrit.server.restapi.change.OnPostReview;
import com.google.gerrit.server.restapi.change.SuggestReviewers;
@@ -217,6 +214,9 @@
import com.google.gerrit.server.submit.MergeSuperSetComputation;
import com.google.gerrit.server.submit.SubmitStrategy;
import com.google.gerrit.server.submit.SubscriptionGraph;
+import com.google.gerrit.server.submitrequirement.predicate.DistinctVotersPredicate;
+import com.google.gerrit.server.submitrequirement.predicate.FileEditsPredicate;
+import com.google.gerrit.server.submitrequirement.predicate.HasSubmoduleUpdatePredicate;
import com.google.gerrit.server.tools.ToolsCatalog;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.util.IdGenerator;
diff --git a/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
index 3f4c158..698628e 100644
--- a/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
@@ -19,8 +19,12 @@
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.server.query.FileEditsPredicate;
-import com.google.gerrit.server.query.FileEditsPredicate.FileEditsArgs;
+import com.google.gerrit.server.submitrequirement.predicate.ConstantPredicate;
+import com.google.gerrit.server.submitrequirement.predicate.DistinctVotersPredicate;
+import com.google.gerrit.server.submitrequirement.predicate.FileEditsPredicate;
+import com.google.gerrit.server.submitrequirement.predicate.FileEditsPredicate.FileEditsArgs;
+import com.google.gerrit.server.submitrequirement.predicate.HasSubmoduleUpdatePredicate;
+import com.google.gerrit.server.submitrequirement.predicate.RegexAuthorEmailPredicate;
import com.google.inject.Inject;
import java.util.List;
import java.util.Locale;
diff --git a/java/com/google/gerrit/server/query/change/ConstantPredicate.java b/java/com/google/gerrit/server/submitrequirement/predicate/ConstantPredicate.java
similarity index 85%
rename from java/com/google/gerrit/server/query/change/ConstantPredicate.java
rename to java/com/google/gerrit/server/submitrequirement/predicate/ConstantPredicate.java
index f0a85fe..c493fa4 100644
--- a/java/com/google/gerrit/server/query/change/ConstantPredicate.java
+++ b/java/com/google/gerrit/server/submitrequirement/predicate/ConstantPredicate.java
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.query.change;
+package com.google.gerrit.server.submitrequirement.predicate;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.SubmitRequirementPredicate;
import com.google.inject.Singleton;
/**
diff --git a/java/com/google/gerrit/server/query/change/DistinctVotersPredicate.java b/java/com/google/gerrit/server/submitrequirement/predicate/DistinctVotersPredicate.java
similarity index 95%
rename from java/com/google/gerrit/server/query/change/DistinctVotersPredicate.java
rename to java/com/google/gerrit/server/submitrequirement/predicate/DistinctVotersPredicate.java
index 5a51f5d..e392989 100644
--- a/java/com/google/gerrit/server/query/change/DistinctVotersPredicate.java
+++ b/java/com/google/gerrit/server/submitrequirement/predicate/DistinctVotersPredicate.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.query.change;
+package com.google.gerrit.server.submitrequirement.predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
@@ -22,6 +22,8 @@
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.SubmitRequirementPredicate;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.Optional;
diff --git a/java/com/google/gerrit/server/query/FileEditsPredicate.java b/java/com/google/gerrit/server/submitrequirement/predicate/FileEditsPredicate.java
similarity index 98%
rename from java/com/google/gerrit/server/query/FileEditsPredicate.java
rename to java/com/google/gerrit/server/submitrequirement/predicate/FileEditsPredicate.java
index 7058765..515dc4a 100644
--- a/java/com/google/gerrit/server/query/FileEditsPredicate.java
+++ b/java/com/google/gerrit/server/submitrequirement/predicate/FileEditsPredicate.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.query;
+package com.google.gerrit.server.submitrequirement.predicate;
import com.google.auto.value.AutoValue;
import com.google.common.collect.Iterables;
diff --git a/java/com/google/gerrit/server/query/change/HasSubmoduleUpdatePredicate.java b/java/com/google/gerrit/server/submitrequirement/predicate/HasSubmoduleUpdatePredicate.java
similarity index 95%
rename from java/com/google/gerrit/server/query/change/HasSubmoduleUpdatePredicate.java
rename to java/com/google/gerrit/server/submitrequirement/predicate/HasSubmoduleUpdatePredicate.java
index 4ff40a4..1774628 100644
--- a/java/com/google/gerrit/server/query/change/HasSubmoduleUpdatePredicate.java
+++ b/java/com/google/gerrit/server/submitrequirement/predicate/HasSubmoduleUpdatePredicate.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.query.change;
+package com.google.gerrit.server.submitrequirement.predicate;
import static com.google.gerrit.server.query.change.SubmitRequirementChangeQueryBuilder.SUBMODULE_UPDATE_HAS_ARG;
@@ -23,6 +23,8 @@
import com.google.gerrit.server.patch.DiffOperations;
import com.google.gerrit.server.patch.DiffOptions;
import com.google.gerrit.server.patch.filediff.FileDiffOutput;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.SubmitRequirementPredicate;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/query/change/RegexAuthorEmailPredicate.java b/java/com/google/gerrit/server/submitrequirement/predicate/RegexAuthorEmailPredicate.java
similarity index 89%
rename from java/com/google/gerrit/server/query/change/RegexAuthorEmailPredicate.java
rename to java/com/google/gerrit/server/submitrequirement/predicate/RegexAuthorEmailPredicate.java
index 22891bc..eb7f666 100644
--- a/java/com/google/gerrit/server/query/change/RegexAuthorEmailPredicate.java
+++ b/java/com/google/gerrit/server/submitrequirement/predicate/RegexAuthorEmailPredicate.java
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.query.change;
+package com.google.gerrit.server.submitrequirement.predicate;
import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.SubmitRequirementPredicate;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index b828037..936b448 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -36,6 +36,7 @@
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.server.CacheRefreshExecutor;
+import com.google.gerrit.server.DefaultRefLogIdentityProvider;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
@@ -193,6 +194,7 @@
install(new AuthModule(authConfig));
install(new GerritApiModule());
install(new ProjectQueryBuilderModule());
+ install(new DefaultRefLogIdentityProvider.Module());
factory(PluginUser.Factory.class);
install(new PluginApiModule());
install(new DefaultPermissionBackendModule());
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
index d055875..62ef118 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
@@ -33,6 +33,7 @@
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.RefLogIdentityProvider;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.DefaultRealm;
@@ -57,6 +58,7 @@
public class EmailIT extends AbstractDaemonTest {
@Inject private @AnonymousCowardName String anonymousCowardName;
+ @Inject private RefLogIdentityProvider refLogIdentityProvider;
@Inject private @CanonicalWebUrl Provider<String> canonicalUrl;
@Inject private @EnablePeerIPInReflogRecord boolean enablePeerIPInReflogRecord;
@Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
@@ -283,6 +285,7 @@
authConfig,
realm,
anonymousCowardName,
+ refLogIdentityProvider,
canonicalUrl,
enablePeerIPInReflogRecord,
accountCache,
diff --git a/javatests/com/google/gerrit/server/IdentifiedUserTest.java b/javatests/com/google/gerrit/server/IdentifiedUserTest.java
index 855a0bc..30ae4aa 100644
--- a/javatests/com/google/gerrit/server/IdentifiedUserTest.java
+++ b/javatests/com/google/gerrit/server/IdentifiedUserTest.java
@@ -92,6 +92,7 @@
bind(AccountCache.class).toInstance(accountCache);
bind(GroupBackend.class).to(SystemGroupBackend.class).in(SINGLETON);
bind(Realm.class).toInstance(mockRealm);
+ install(new DefaultRefLogIdentityProvider.Module());
}
};
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index be8f1f9..1e6ba3a 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -31,6 +31,7 @@
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.DefaultRefLogIdentityProvider;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -180,6 +181,7 @@
install(new DefaultUrlFormatterModule());
install(NoteDbModule.forTest());
+ install(new DefaultRefLogIdentityProvider.Module());
bind(AllUsersName.class).toProvider(AllUsersNameProvider.class);
bind(String.class).annotatedWith(GerritServerId.class).toInstance(serverId);
bind(new TypeLiteral<ImmutableSet<String>>() {})
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
index fac7a83..00cb938 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -307,6 +307,8 @@
private view?: GerritView;
+ readonly page = page.create();
+
constructor(
private readonly reporting: ReportingService,
private readonly routerModel: RouterModel,
@@ -343,7 +345,7 @@
}
if (browserUrl.toString() !== stateUrl.toString()) {
- page.replace(
+ this.page.replace(
stateUrl.toString(),
null,
/* init: */ false,
@@ -360,6 +362,7 @@
subscription.unsubscribe();
}
this.subscriptions = [];
+ this.page.stop();
}
start() {
@@ -402,7 +405,7 @@
redirect(url: string) {
this._isRedirecting = true;
- page.redirect(url);
+ this.page.redirect(url);
}
/**
@@ -431,7 +434,9 @@
*/
redirectToLogin(returnUrl: string) {
const basePath = getBaseUrl() || '';
- page('/login/' + encodeURIComponent(returnUrl.substring(basePath.length)));
+ this.page(
+ '/login/' + encodeURIComponent(returnUrl.substring(basePath.length))
+ );
}
/**
@@ -504,7 +509,7 @@
handler: (ctx: PageContext) => void,
authRedirect?: boolean
) {
- page(
+ this.page(
pattern,
(ctx, next) => this.loadUserMiddleware(ctx, next),
ctx => {
@@ -555,14 +560,14 @@
* page.show() eventually just calls `window.history.pushState()`.
*/
setUrl(url: string) {
- page.show(url);
+ this.page.show(url);
}
/**
* Navigate to this URL, but replace the current URL in the history instead of
* adding a new one (which is what `setUrl()` would do).
*
- * page.redirect() eventually just calls `window.history.replaceState()`.
+ * this.page.redirect() eventually just calls `window.history.replaceState()`.
*/
replaceUrl(url: string) {
this.redirect(url);
@@ -585,10 +590,10 @@
startRouter() {
const base = getBaseUrl();
if (base) {
- page.base(base);
+ this.page.base(base);
}
- page.exit('*', (_, next) => {
+ this.page.exit('*', (_, next) => {
if (!this._isRedirecting) {
this.reporting.beforeLocationChanged();
}
@@ -599,7 +604,7 @@
// Remove the tracking param 'usp' (User Source Parameter) from the URL,
// just to have users look at cleaner URLs.
- page((ctx, next) => {
+ this.page((ctx, next) => {
if (window.URLSearchParams) {
const pathname = toPathname(ctx.canonicalPath);
const searchParams = toSearchParams(ctx.canonicalPath);
@@ -615,7 +620,7 @@
});
// Middleware
- page((ctx, next) => {
+ this.page((ctx, next) => {
document.body.scrollTop = 0;
if (ctx.hash.match(RoutePattern.PLUGIN_SCREEN)) {
@@ -986,7 +991,7 @@
this.handleDefaultRoute()
);
- page.start();
+ this.page.start();
}
/**
@@ -1153,7 +1158,7 @@
const state: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.GROUPS,
- offset: ctx.params[1] || 0,
+ offset: ctx.params[1] ?? '0',
filter: null,
openCreateModal: ctx.hash === 'create',
};
@@ -1168,6 +1173,7 @@
adminView: AdminChildView.GROUPS,
offset: ctx.params['offset'],
filter: ctx.params['filter'],
+ openCreateModal: false,
};
// Note that router model view must be updated before view models.
this.setState(state);
@@ -1178,7 +1184,9 @@
const state: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.GROUPS,
+ offset: ctx.params[1] ?? '0',
filter: ctx.params['filter'] || null,
+ openCreateModal: false,
};
// Note that router model view must be updated before view models.
this.setState(state);
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
index 112c776..af44739 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
@@ -5,12 +5,13 @@
*/
import '../../../test/common-test-setup';
import './gr-router';
-import {page, PageContext} from '../../../utils/page-wrapper-utils';
+import {Page, PageContext} from '../../../utils/page-wrapper-utils';
import {
stubBaseUrl,
stubRestApi,
addListenerForTest,
waitEventLoop,
+ waitUntilCalled,
} from '../../../test/test-utils';
import {GrRouter, routerToken, _testOnly_RoutePattern} from './gr-router';
import {GerritView} from '../../../services/router/router-model';
@@ -25,7 +26,7 @@
} from '../../../types/common';
import {AppElementParams} from '../../gr-app-types';
import {assert} from '@open-wc/testing';
-import {AdminChildView} from '../../../models/views/admin';
+import {AdminChildView, AdminViewState} from '../../../models/views/admin';
import {RepoDetailView} from '../../../models/views/repo';
import {GroupDetailView} from '../../../models/views/group';
import {ChangeChildView, ChangeViewState} from '../../../models/views/change';
@@ -38,12 +39,15 @@
createRevision,
} from '../../../test/test-data-generators';
import {ParsedChangeInfo} from '../../../types/types';
+import {ViewState} from '../../../models/views/base';
suite('gr-router tests', () => {
let router: GrRouter;
+ let page: Page;
setup(() => {
router = testResolver(routerToken);
+ page = router.page;
});
test('getHashFromCanonicalPath', () => {
@@ -289,6 +293,19 @@
assert.deepEqual(setStateStub.lastCall.args[0], params);
}
+ async function checkUrlToState<T extends ViewState>(url: string, state: T) {
+ setStateStub.reset();
+ router.page.show(url);
+ await waitUntilCalled(setStateStub, 'setState');
+ assert.deepEqual(setStateStub.lastCall.firstArg, state);
+ }
+
+ async function checkUrlNotMatched(url: string) {
+ handlePassThroughRoute.reset();
+ router.page.show(url);
+ await waitUntilCalled(handlePassThroughRoute, 'handlePassThroughRoute');
+ }
+
function createPageContext(): PageContext {
return {
canonicalPath: '',
@@ -304,6 +321,7 @@
redirectStub = sinon.stub(router, 'redirect');
setStateStub = sinon.stub(router, 'setState');
handlePassThroughRoute = sinon.stub(router, 'handlePassThroughRoute');
+ router.startRouter();
});
test('handleLegacyProjectDashboardRoute', () => {
@@ -735,55 +753,55 @@
});
});
- test('handleGroupListOffsetRoute', () => {
- const ctx = createPageContext();
- assertctxToParams(ctx, 'handleGroupListOffsetRoute', {
+ test('list of groups', async () => {
+ const defaultState: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.GROUPS,
- offset: 0,
- filter: null,
+ offset: '0',
openCreateModal: false,
- });
-
- ctx.params[1] = '42';
- assertctxToParams(ctx, 'handleGroupListOffsetRoute', {
- view: GerritView.ADMIN,
- adminView: AdminChildView.GROUPS,
- offset: '42',
filter: null,
- openCreateModal: false,
- });
+ };
- ctx.hash = 'create';
- assertctxToParams(ctx, 'handleGroupListOffsetRoute', {
- view: GerritView.ADMIN,
- adminView: AdminChildView.GROUPS,
- offset: '42',
- filter: null,
+ await checkUrlToState('/admin/groups', defaultState);
+ await checkUrlToState('/admin/groups/', defaultState);
+ await checkUrlToState('/admin/groups#create', {
+ ...defaultState,
openCreateModal: true,
});
- });
-
- test('handleGroupListFilterOffsetRoute', () => {
- const ctx = {
- ...createPageContext(),
- params: {filter: 'foo', offset: '42'},
- };
- assertctxToParams(ctx, 'handleGroupListFilterOffsetRoute', {
- view: GerritView.ADMIN,
- adminView: AdminChildView.GROUPS,
- offset: '42',
- filter: 'foo',
+ await checkUrlToState('/admin/groups,123', {
+ ...defaultState,
+ offset: '123',
});
- });
-
- test('handleGroupListFilterRoute', () => {
- const ctx = {...createPageContext(), params: {filter: 'foo'}};
- assertctxToParams(ctx, 'handleGroupListFilterRoute', {
- view: GerritView.ADMIN,
- adminView: AdminChildView.GROUPS,
- filter: 'foo',
+ await checkUrlToState('/admin/groups,123#create', {
+ ...defaultState,
+ offset: '123',
+ openCreateModal: true,
});
+
+ await checkUrlToState('/admin/groups/q/filter:asdf', {
+ ...defaultState,
+ filter: 'asdf',
+ });
+ await checkUrlToState('/admin/groups/q/filter:asdf,123', {
+ ...defaultState,
+ filter: 'asdf',
+ offset: '123',
+ });
+ // #create is ignored when filtering
+ await checkUrlToState('/admin/groups/q/filter:asdf,123#create', {
+ ...defaultState,
+ filter: 'asdf',
+ offset: '123',
+ });
+ // filter is decoded (twice)
+ await checkUrlToState(
+ '/admin/groups/q/filter:XX%20XX%2520XX%252FXX%3FXX',
+ {...defaultState, filter: 'XX XX XX/XX?XX'}
+ );
+
+ // Slash must be double encoded in `filter` param.
+ await checkUrlNotMatched('/admin/groups/q/filter:asdf/qwer,11');
+ await checkUrlNotMatched('/admin/groups/q/filter:asdf%2Fqwer,11');
});
test('handleGroupRoute', () => {
diff --git a/polygerrit-ui/app/utils/page-wrapper-utils.ts b/polygerrit-ui/app/utils/page-wrapper-utils.ts
index 78e78ed..2e5c7b42 100644
--- a/polygerrit-ui/app/utils/page-wrapper-utils.ts
+++ b/polygerrit-ui/app/utils/page-wrapper-utils.ts
@@ -17,6 +17,7 @@
replace(path: string, state: null, init: boolean, dispatch: boolean): void;
base(url: string): void;
start(): void;
+ stop(): void;
exit(pattern: string | RegExp, ...pageCallback: PageCallback[]): void;
}
@@ -37,6 +38,7 @@
next: PageNextCallback
) => void;
-// TODO: Convert page usages to the real types and remove this file of wrapper
-// types. Also remove workarounds in rollup config.
-export const page = pagejs as unknown as Page;
+// Must only be used by gr-router!
+// TODO: Move this into gr-router. Note that there is a Google import rule
+// that would need to be modified.
+export const page = pagejs as unknown as {create(): Page};