Merge "Always use new author timestamp for super repo commit"
diff --git a/Documentation/cmd-index-project.txt b/Documentation/cmd-index-changes-in-project.txt
similarity index 60%
rename from Documentation/cmd-index-project.txt
rename to Documentation/cmd-index-changes-in-project.txt
index 2196a26..ec1626f 100644
--- a/Documentation/cmd-index-project.txt
+++ b/Documentation/cmd-index-changes-in-project.txt
@@ -1,12 +1,12 @@
-= gerrit index project
+= gerrit index changes in project
== NAME
-gerrit index project - Index all the changes in one or more projects.
+gerrit index changes in project - Index all the changes in one or more projects.
== SYNOPSIS
[verse]
--
-_ssh_ -p <port> <host> _gerrit index project_ <PROJECT> [<PROJECT> ...]
+_ssh_ -p <port> <host> _gerrit index changes-in-project_ <PROJECT> [<PROJECT> ...]
--
== DESCRIPTION
@@ -26,7 +26,7 @@
Index all changes in projects MyProject and NiceProject.
----
- $ ssh -p 29418 user@review.example.com gerrit index project MyProject NiceProject
+ $ ssh -p 29418 user@review.example.com gerrit index changes-in-project MyProject NiceProject
----
GERRIT
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index f535281..2362401 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -136,7 +136,7 @@
link:cmd-index-changes.html[gerrit index changes]::
Index one or more changes.
-link:cmd-index-project.html[gerrit index project]::
+link:cmd-index-changes-in-project.html[gerrit index changes-in-project]::
Index all the changes in one or more projects.
link:cmd-logging-ls-level.html[gerrit logging ls-level]::
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 2737f58..71441d83 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2779,16 +2779,16 @@
it to 0 disables the dedicated thread pool and indexing will be done in the same
thread as the operation.
+
-If not set or set to a negative value, defaults to 1 plus half of the number of
-logical CPUs as returned by the JVM.
+If not set or set to a zero, defaults to the number of logical CPUs as returned
+by the JVM. If set to a negative value, defaults to a direct executor.
[[index.batchThreads]]index.batchThreads::
+
Number of threads to use for indexing in background operations, such as
online schema upgrades.
+
-If not set or set to a negative value, defaults to the number of logical
-CPUs as returned by the JVM.
+If not set or set to a zero, defaults to the number of logical CPUs as returned
+by the JVM. If set to a negative value, defaults to a direct executor.
[[index.onlineUpgrade]]index.onlineUpgrade::
+
@@ -3009,41 +3009,13 @@
+
Not set by default.
-[[elasticsearch.requestCompression]]elasticsearch.requestCompression::
+[[elasticsearch.maxRetryTimeout]]elasticsearch.maxRetryTimeout::
+
-Enable request compression.
-+
-Defaults to `false`.
-
-[[elasticsearch.connectionTimeout]]elasticsearch.connectionTimeout::
-+
-How long should Gerrit waits for connection.
+Sets the maximum timeout to honor in case of multiple retries of the same request.
+
The value is in the usual time-unit format like `1 m`, `5 m`.
+
-Defaults to `5 m`
-
-[[elasticsearch.maxConnectionIdleTime]]elasticsearch.maxConnectionIdleTime::
-+
-How long connection can stay in idle.
-+
-The value is in the usual time-unit format like `1 m`, `5 m`.
-+
-Defaults to `5 m`
-
-[[elasticsearch.maxTotalConnection]]elasticsearch.maxTotalConnection::
-+
-How many connections can be spawned simultaneously.
-+
-Defaults to `1`
-
-[[elasticsearch.maxReadTimeout]]elasticsearch.maxReadTimeout::
-+
-Timeout for read operations.
-+
-The value is in the usual time-unit format like `1 m`, `5 m`.
-+
-Defaults to `5 m`
+Defaults to `30000 ms`.
==== Elasticsearch server(s) configuration
@@ -3058,7 +3030,7 @@
[[elasticsearch.name.hostname]]elasticsearch.name.hostname::
+
Elasticsearch server hostname.
-
++
Defaults to `localhost`.
[[elasticsearch.name.port]]elasticsearch.name.port::
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index bc5a3c6..54f8022 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1354,6 +1354,31 @@
[[index]]
+=== Index project
+
+Adds or updates the current project (and children, if specified) in the secondary index.
+The indexing task is executed asynchronously in background, so this command
+returns immediately.
+
+As an input, a link:#index-project-input[IndexProjectInput] entity can be provided.
+
+.Request
+----
+ POST /projects/MyProject/index HTTP/1.0
+ Content-Type: application/json; charset=UTF-8
+
+ {
+ "index_children": "true"
+ }
+----
+
+.Response
+----
+ HTTP/1.1 202 Accepted
+ Content-Disposition: attachment
+----
+
+[[index.changes]]
=== Index all changes in a project
Adds or updates all the changes belonging to a project in the secondary index.
@@ -1362,7 +1387,7 @@
.Request
----
- POST /projects/MyProject/index HTTP/1.0
+ POST /projects/MyProject/index.changes HTTP/1.0
----
.Response
@@ -3128,6 +3153,17 @@
omitted.
|============================
+[[index-project-input]]
+=== IndexProjectInput
+The `IndexProjectInput` contains parameters for indexing a project.
+
+[options="header",cols="1,^2,4"]
+|================================
+|Field Name ||Description
+|`index_children` ||
+If children should be indexed recursively.
+|================================
+
[[inherited-boolean-info]]
=== InheritedBooleanInfo
A boolean value that can also be inherited.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
index e518d26..1b946cd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -169,7 +169,7 @@
@Override
public void setValue(AccessSection value) {
- Collections.sort(value.getPermissions());
+ sortPermissions(value);
this.value = value;
this.readOnly = !editing || !(projectAccess.isOwnerOf(value) || projectAccess.canUpload());
@@ -204,6 +204,12 @@
}
}
+ private void sortPermissions(AccessSection accessSection) {
+ List<Permission> permissionList = new ArrayList<>(accessSection.getPermissions());
+ Collections.sort(permissionList);
+ accessSection.setPermissions(permissionList);
+ }
+
void setEditing(boolean editing) {
this.editing = editing;
}
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 6828393..abb28ce 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -397,6 +397,8 @@
baseConfig.setString("sshd", null, "listenAddress", "off");
}
+ baseConfig.setInt("index", null, "batchThreads", -1);
+
baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
Module module = createModule();
if (classDesc.equals(methodDesc) && !classDesc.sandboxed() && !methodDesc.sandboxed()) {
@@ -1075,7 +1077,7 @@
ProjectConfig config = ProjectConfig.read(md);
AccessSection s = config.getAccessSection(ref, true);
Permission p = s.getPermission(permission, true);
- p.getRules().clear();
+ p.clearRules();
config.commit(md);
projectCache.evict(config.getProject());
}
diff --git a/java/com/google/gerrit/common/data/AccessSection.java b/java/com/google/gerrit/common/data/AccessSection.java
index 82dc620..f658066 100644
--- a/java/com/google/gerrit/common/data/AccessSection.java
+++ b/java/com/google/gerrit/common/data/AccessSection.java
@@ -34,11 +34,12 @@
super(refPattern);
}
+ // TODO(ekempin): Make this method return an ImmutableList once the GWT UI is gone.
public List<Permission> getPermissions() {
if (permissions == null) {
- permissions = new ArrayList<>();
+ return new ArrayList<>();
}
- return permissions;
+ return new ArrayList<>(permissions);
}
public void setPermissions(List<Permission> list) {
@@ -49,7 +50,7 @@
}
}
- permissions = list;
+ permissions = new ArrayList<>(list);
}
@Nullable
@@ -59,13 +60,19 @@
@Nullable
public Permission getPermission(String name, boolean create) {
- for (Permission p : getPermissions()) {
- if (p.getName().equalsIgnoreCase(name)) {
- return p;
+ if (permissions != null) {
+ for (Permission p : permissions) {
+ if (p.getName().equalsIgnoreCase(name)) {
+ return p;
+ }
}
}
if (create) {
+ if (permissions == null) {
+ permissions = new ArrayList<>();
+ }
+
Permission p = new Permission(name);
permissions.add(p);
return p;
@@ -75,7 +82,10 @@
}
public void addPermission(Permission permission) {
- List<Permission> permissions = getPermissions();
+ if (permissions == null) {
+ permissions = new ArrayList<>();
+ }
+
for (Permission p : permissions) {
if (p.getName().equalsIgnoreCase(permission.getName())) {
throw new IllegalArgumentException();
@@ -133,4 +143,15 @@
return new HashSet<>(getPermissions())
.equals(new HashSet<>(((AccessSection) obj).getPermissions()));
}
+
+ @Override
+ public int hashCode() {
+ int hashCode = super.hashCode();
+ if (permissions != null) {
+ for (Permission permission : permissions) {
+ hashCode += permission.hashCode();
+ }
+ }
+ return hashCode;
+ }
}
diff --git a/java/com/google/gerrit/common/data/Permission.java b/java/com/google/gerrit/common/data/Permission.java
index dff30d7..af23e39 100644
--- a/java/com/google/gerrit/common/data/Permission.java
+++ b/java/com/google/gerrit/common/data/Permission.java
@@ -155,13 +155,16 @@
exclusiveGroup = newExclusiveGroup;
}
+ // TODO(ekempin): Make this method return an ImmutableList once the GWT UI is gone.
public List<PermissionRule> getRules() {
- initRules();
- return rules;
+ if (rules == null) {
+ return new ArrayList<>();
+ }
+ return new ArrayList<>(rules);
}
public void setRules(List<PermissionRule> list) {
- rules = list;
+ rules = new ArrayList<>(list);
}
public void add(PermissionRule rule) {
@@ -181,6 +184,12 @@
}
}
+ public void clearRules() {
+ if (rules != null) {
+ rules.clear();
+ }
+ }
+
public PermissionRule getRule(GroupReference group) {
return getRule(group, false);
}
diff --git a/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java b/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java
index 4184ec0..0148bf1 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java
@@ -16,6 +16,7 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -29,21 +30,18 @@
@Singleton
class ElasticConfiguration {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private static final String DEFAULT_HOST = "localhost";
private static final String DEFAULT_PORT = "9200";
private static final String DEFAULT_PROTOCOL = "http";
private final Config cfg;
+ private final List<HttpHost> hosts;
- final List<HttpHost> urls;
final String username;
final String password;
- final boolean requestCompression;
- final long connectionTimeout;
- final long maxConnectionIdleTime;
- final TimeUnit maxConnectionIdleUnit = TimeUnit.MILLISECONDS;
- final int maxTotalConnection;
- final int readTimeout;
+ final int maxRetryTimeout;
final String prefix;
@Inject
@@ -51,39 +49,39 @@
this.cfg = cfg;
this.username = cfg.getString("elasticsearch", null, "username");
this.password = cfg.getString("elasticsearch", null, "password");
- this.requestCompression = cfg.getBoolean("elasticsearch", null, "requestCompression", false);
- this.connectionTimeout =
- cfg.getTimeUnit("elasticsearch", null, "connectionTimeout", 3000, TimeUnit.MILLISECONDS);
- this.maxConnectionIdleTime =
- cfg.getTimeUnit(
- "elasticsearch", null, "maxConnectionIdleTime", 3000, TimeUnit.MILLISECONDS);
- this.maxTotalConnection = cfg.getInt("elasticsearch", null, "maxTotalConnection", 1);
- this.readTimeout =
- (int) cfg.getTimeUnit("elasticsearch", null, "readTimeout", 3000, TimeUnit.MICROSECONDS);
+ this.maxRetryTimeout =
+ (int)
+ cfg.getTimeUnit("elasticsearch", null, "maxRetryTimeout", 30000, TimeUnit.MILLISECONDS);
this.prefix = Strings.nullToEmpty(cfg.getString("elasticsearch", null, "prefix"));
Set<String> subsections = cfg.getSubsections("elasticsearch");
if (subsections.isEmpty()) {
HttpHost httpHost =
new HttpHost(DEFAULT_HOST, Integer.valueOf(DEFAULT_PORT), DEFAULT_PROTOCOL);
- this.urls = Collections.singletonList(httpHost);
+ this.hosts = Collections.singletonList(httpHost);
} else {
- this.urls = new ArrayList<>(subsections.size());
+ this.hosts = new ArrayList<>(subsections.size());
for (String subsection : subsections) {
String port = getString(cfg, subsection, "port", DEFAULT_PORT);
String host = getString(cfg, subsection, "hostname", DEFAULT_HOST);
String protocol = getString(cfg, subsection, "protocol", DEFAULT_PROTOCOL);
HttpHost httpHost = new HttpHost(host, Integer.valueOf(port), protocol);
- this.urls.add(httpHost);
+ this.hosts.add(httpHost);
}
}
+
+ logger.atInfo().log("Elasticsearch hosts: %s", hosts);
}
Config getConfig() {
return cfg;
}
+ HttpHost[] getHosts() {
+ return hosts.toArray(new HttpHost[hosts.size()]);
+ }
+
String getIndexName(String name, int schemaVersion) {
return String.format("%s%s_%04d", prefix, name, schemaVersion);
}
diff --git a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
index c2c4548..db59257 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
@@ -22,7 +22,6 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
@@ -38,18 +37,14 @@
class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListener {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final HttpHost[] hosts;
- private final String username;
- private final String password;
+ private final ElasticConfiguration cfg;
private RestClient client;
private ElasticQueryAdapter adapter;
@Inject
ElasticRestClientProvider(ElasticConfiguration cfg) {
- hosts = cfg.urls.toArray(new HttpHost[cfg.urls.size()]);
- username = cfg.username;
- password = cfg.password;
+ this.cfg = cfg;
}
public static LifecycleModule module() {
@@ -131,12 +126,15 @@
}
private RestClient build() {
- RestClientBuilder builder = RestClient.builder(hosts);
+ RestClientBuilder builder = RestClient.builder(cfg.getHosts());
+ builder.setMaxRetryTimeoutMillis(cfg.maxRetryTimeout);
setConfiguredCredentialsIfAny(builder);
return builder.build();
}
private void setConfiguredCredentialsIfAny(RestClientBuilder builder) {
+ String username = cfg.username;
+ String password = cfg.password;
if (username != null && password != null) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
diff --git a/java/com/google/gerrit/extensions/api/projects/IndexProjectInput.java b/java/com/google/gerrit/extensions/api/projects/IndexProjectInput.java
new file mode 100644
index 0000000..a41f227
--- /dev/null
+++ b/java/com/google/gerrit/extensions/api/projects/IndexProjectInput.java
@@ -0,0 +1,19 @@
+// 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.extensions.api.projects;
+
+public class IndexProjectInput {
+ public Boolean indexChildren;
+}
diff --git a/java/com/google/gerrit/extensions/api/projects/ProjectApi.java b/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
index c9f47c2..63d40f0 100644
--- a/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
+++ b/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
@@ -191,6 +191,13 @@
void parent(String parent) throws RestApiException;
/**
+ * Reindex the project and children in case {@code indexChildren} is specified.
+ *
+ * @param indexChildren decides if children should be indexed recursively
+ */
+ void index(boolean indexChildren) throws RestApiException;
+
+ /**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
*/
@@ -344,5 +351,10 @@
public void parent(String parent) throws RestApiException {
throw new NotImplementedException();
}
+
+ @Override
+ public void index(boolean indexChildren) throws RestApiException {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/java/com/google/gerrit/extensions/restapi/AcceptsDelete.java b/java/com/google/gerrit/extensions/restapi/AcceptsDelete.java
index 6b5da7c..e1c4b3b 100644
--- a/java/com/google/gerrit/extensions/restapi/AcceptsDelete.java
+++ b/java/com/google/gerrit/extensions/restapi/AcceptsDelete.java
@@ -17,17 +17,31 @@
/**
* Optional interface for {@link RestCollection}.
*
- * <p>Collections that implement this interface can accept a {@code DELETE} directly on the
- * collection itself.
+ * <p>This interface is used for 2 purposes:
+ *
+ * <ul>
+ * <li>to support {@code DELETE} directly on the collection itself
+ * <li>to support {@code DELETE} on a non-existing member of the collection (in order to create
+ * that member)
+ * </ul>
+ *
+ * <p>This interface is not supported for root collections.
*/
public interface AcceptsDelete<P extends RestResource> {
/**
- * Handle deletion of a child resource by DELETE on the collection.
+ * Handle
*
- * @param parent parent collection handle.
- * @param id id of the resource being created (optional).
- * @return a view to perform the deletion.
- * @throws RestApiException the view cannot be constructed.
+ * <ul>
+ * <li>{@code DELETE} directly on the collection itself (in this case id is {@code null})
+ * <li>{@code DELETE} on a non-existing member of the collection (in this case id is not {@code
+ * null})
+ * </ul>
+ *
+ * @param parent the collection
+ * @param id id of the non-existing collection member for which the {@code DELETE} request is
+ * done, {@code null} if the {@code DELETE} request is done on the collection itself
+ * @return a view to handle the {@code DELETE} request
+ * @throws RestApiException the view cannot be constructed
*/
RestModifyView<P, ?> delete(P parent, IdString id) throws RestApiException;
}
diff --git a/java/com/google/gerrit/gpg/server/GpgKeys.java b/java/com/google/gerrit/gpg/server/GpgKeys.java
index a2d901f..3f090a1 100644
--- a/java/com/google/gerrit/gpg/server/GpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -59,8 +59,6 @@
public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static final String MIME_TYPE = "application/pgp-keys";
-
private final DynamicMap<RestView<GpgKey>> views;
private final Provider<CurrentUser> self;
private final Provider<PublicKeyStore> storeProvider;
diff --git a/java/com/google/gerrit/httpd/raw/ResourceServlet.java b/java/com/google/gerrit/httpd/raw/ResourceServlet.java
index 64b5bbb..3244232 100644
--- a/java/com/google/gerrit/httpd/raw/ResourceServlet.java
+++ b/java/com/google/gerrit/httpd/raw/ResourceServlet.java
@@ -34,6 +34,7 @@
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.httpd.HtmlDomUtil;
+import com.google.gerrit.server.UsedAt;
import com.google.gerrit.util.http.CacheHeaders;
import com.google.gwtjsonrpc.server.RPCServletUtils;
import java.io.IOException;
@@ -324,6 +325,7 @@
}
}
+ @UsedAt(UsedAt.Project.GOOGLE)
public static class Weigher implements com.google.common.cache.Weigher<Path, Resource> {
@Override
public int weigh(Path p, Resource r) {
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 1196e47..a454c00 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -337,7 +337,7 @@
}
}
if (viewData.view == null) {
- viewData = view(rsrc, rc, req.getMethod(), path);
+ viewData = view(rc, req.getMethod(), path);
}
}
checkRequiresCapability(viewData);
@@ -392,7 +392,7 @@
}
}
if (viewData.view == null) {
- viewData = view(rsrc, c, req.getMethod(), path);
+ viewData = view(c, req.getMethod(), path);
}
checkRequiresCapability(viewData);
}
@@ -1109,10 +1109,7 @@
}
private ViewData view(
- RestResource rsrc,
- RestCollection<RestResource, RestResource> rc,
- String method,
- List<IdString> path)
+ RestCollection<RestResource, RestResource> rc, String method, List<IdString> path)
throws AmbiguousViewException, RestApiException {
DynamicMap<RestView<RestResource>> views = rc.views();
final IdString projection = path.isEmpty() ? IdString.fromUrl("/") : path.remove(0);
@@ -1136,10 +1133,8 @@
return new ViewData(p.get(0), view);
}
view = views.get(p.get(0), "GET." + viewname);
- if (view != null && view instanceof AcceptsPost && "POST".equals(method)) {
- @SuppressWarnings("unchecked")
- AcceptsPost<RestResource> ap = (AcceptsPost<RestResource>) view;
- return new ViewData(p.get(0), ap.post(rsrc));
+ if (view != null) {
+ return new ViewData(p.get(0), view);
}
throw new ResourceNotFoundException(projection);
}
@@ -1150,10 +1145,8 @@
return new ViewData(null, core);
}
core = views.get("gerrit", "GET." + p.get(0));
- if (core instanceof AcceptsPost && "POST".equals(method)) {
- @SuppressWarnings("unchecked")
- AcceptsPost<RestResource> ap = (AcceptsPost<RestResource>) core;
- return new ViewData(null, ap.post(rsrc));
+ if (core != null) {
+ return new ViewData(null, core);
}
Map<String, RestView<RestResource>> r = new TreeMap<>();
diff --git a/java/com/google/gerrit/server/PatchSetUtil.java b/java/com/google/gerrit/server/PatchSetUtil.java
index 252eb61..c9ca972 100644
--- a/java/com/google/gerrit/server/PatchSetUtil.java
+++ b/java/com/google/gerrit/server/PatchSetUtil.java
@@ -214,14 +214,13 @@
return false;
}
- /** Returns the full commit message for the given project at the given patchset revision */
- public String getFullCommitMessage(Project.NameKey project, PatchSet patchSet)
- throws IOException {
+ /** Returns the commit for the given project at the given patchset revision */
+ public RevCommit getRevCommit(Project.NameKey project, PatchSet patchSet) throws IOException {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
RevCommit src = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
rw.parseBody(src);
- return src.getFullMessage();
+ return src;
}
}
}
diff --git a/java/com/google/gerrit/server/UsedAt.java b/java/com/google/gerrit/server/UsedAt.java
new file mode 100644
index 0000000..b564157
--- /dev/null
+++ b/java/com/google/gerrit/server/UsedAt.java
@@ -0,0 +1,45 @@
+// 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;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A marker for a method that is public solely because it is called from inside a project or an
+ * organisation using Gerrit.
+ */
+@BindingAnnotation
+@Target({METHOD, TYPE})
+@Retention(RUNTIME)
+@GwtCompatible
+public @interface UsedAt {
+ /** Enumeration of projects that call a method that would otherwise be private. */
+ enum Project {
+ GOOGLE,
+ PLUGIN_DELETE_PROJECT,
+ PLUGIN_SERVICEUSER,
+ PLUGINS_ALL, // Use this project if a method/type is generally made available to all plugins.
+ }
+
+ /** Reference to the project that uses the method annotated with this annotation. */
+ Project value();
+}
diff --git a/java/com/google/gerrit/server/account/Emails.java b/java/com/google/gerrit/server/account/Emails.java
index 8a48167..e91ce49 100644
--- a/java/com/google/gerrit/server/account/Emails.java
+++ b/java/com/google/gerrit/server/account/Emails.java
@@ -95,6 +95,16 @@
return builder.build();
}
+ /**
+ * Returns the accounts with the given email.
+ *
+ * <p>This method behaves just like {@link #getAccountFor(String)}, except that accounts are not
+ * looked up by their preferred email. Thus, this method does not rely on the accounts index.
+ */
+ public ImmutableSet<Account.Id> getAccountForExternal(String email) throws IOException {
+ return externalIds.byEmail(email).stream().map(ExternalId::accountId).collect(toImmutableSet());
+ }
+
private <T> T executeIndexQuery(Action<T> action) throws OrmException {
try {
return retryHelper.execute(ActionType.INDEX_QUERY, action, OrmException.class::isInstance);
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index fb42ed0..358a3a8 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -57,7 +57,6 @@
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeMessageResource;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.PureRevert;
import com.google.gerrit.server.change.WorkInProgressOp;
import com.google.gerrit.server.restapi.change.Abandon;
import com.google.gerrit.server.restapi.change.ChangeIncludedIn;
@@ -70,6 +69,7 @@
import com.google.gerrit.server.restapi.change.GetAssignee;
import com.google.gerrit.server.restapi.change.GetHashtags;
import com.google.gerrit.server.restapi.change.GetPastAssignees;
+import com.google.gerrit.server.restapi.change.GetPureRevert;
import com.google.gerrit.server.restapi.change.GetTopic;
import com.google.gerrit.server.restapi.change.Ignore;
import com.google.gerrit.server.restapi.change.Index;
@@ -153,7 +153,7 @@
private final SetWorkInProgress setWip;
private final SetReadyForReview setReady;
private final PutMessage putMessage;
- private final PureRevert pureRevert;
+ private final Provider<GetPureRevert> getPureRevertProvider;
private final StarredChangesUtil stars;
@Inject
@@ -200,7 +200,7 @@
SetWorkInProgress setWip,
SetReadyForReview setReady,
PutMessage putMessage,
- PureRevert pureRevert,
+ Provider<GetPureRevert> getPureRevertProvider,
StarredChangesUtil stars,
@Assisted ChangeResource change) {
this.changeApi = changeApi;
@@ -245,7 +245,7 @@
this.setWip = setWip;
this.setReady = setReady;
this.putMessage = putMessage;
- this.pureRevert = pureRevert;
+ this.getPureRevertProvider = getPureRevertProvider;
this.stars = stars;
this.change = change;
}
@@ -714,7 +714,9 @@
@Override
public PureRevertInfo pureRevert(@Nullable String claimedOriginal) throws RestApiException {
try {
- return pureRevert.get(change.getNotes(), claimedOriginal);
+ GetPureRevert getPureRevert = getPureRevertProvider.get();
+ getPureRevert.setClaimedOriginal(claimedOriginal);
+ return getPureRevert.apply(change);
} catch (Exception e) {
throw asRestApiException("Cannot compute pure revert", e);
}
diff --git a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 1065bff..a49f8c4 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -34,6 +34,7 @@
import com.google.gerrit.extensions.api.projects.DeleteTagsInput;
import com.google.gerrit.extensions.api.projects.DescriptionInput;
import com.google.gerrit.extensions.api.projects.HeadInput;
+import com.google.gerrit.extensions.api.projects.IndexProjectInput;
import com.google.gerrit.extensions.api.projects.ParentInput;
import com.google.gerrit.extensions.api.projects.ProjectApi;
import com.google.gerrit.extensions.api.projects.ProjectInput;
@@ -64,6 +65,7 @@
import com.google.gerrit.server.restapi.project.GetDescription;
import com.google.gerrit.server.restapi.project.GetHead;
import com.google.gerrit.server.restapi.project.GetParent;
+import com.google.gerrit.server.restapi.project.Index;
import com.google.gerrit.server.restapi.project.ListBranches;
import com.google.gerrit.server.restapi.project.ListChildProjects;
import com.google.gerrit.server.restapi.project.ListDashboards;
@@ -118,6 +120,7 @@
private final SetHead setHead;
private final GetParent getParent;
private final SetParent setParent;
+ private final Index index;
@AssistedInject
ProjectApiImpl(
@@ -150,6 +153,7 @@
SetHead setHead,
GetParent getParent,
SetParent setParent,
+ Index index,
@Assisted ProjectResource project) {
this(
permissionBackend,
@@ -182,6 +186,7 @@
setHead,
getParent,
setParent,
+ index,
null);
}
@@ -216,6 +221,7 @@
SetHead setHead,
GetParent getParent,
SetParent setParent,
+ Index index,
@Assisted String name) {
this(
permissionBackend,
@@ -248,6 +254,7 @@
setHead,
getParent,
setParent,
+ index,
name);
}
@@ -282,6 +289,7 @@
SetHead setHead,
GetParent getParent,
SetParent setParent,
+ Index index,
String name) {
this.permissionBackend = permissionBackend;
this.createProject = createProject;
@@ -314,6 +322,7 @@
this.getParent = getParent;
this.setParent = setParent;
this.name = name;
+ this.index = index;
}
@Override
@@ -595,6 +604,17 @@
}
}
+ @Override
+ public void index(boolean indexChildren) throws RestApiException {
+ try {
+ IndexProjectInput input = new IndexProjectInput();
+ input.indexChildren = indexChildren;
+ index.apply(checkExists(), input);
+ } catch (Exception e) {
+ throw asRestApiException("Cannot index project", e);
+ }
+ }
+
private ProjectResource checkExists() throws ResourceNotFoundException {
if (project == null) {
throw new ResourceNotFoundException(name);
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
index d7baee2..1365cb6 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -48,7 +48,6 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
-import org.h2.jdbc.JdbcSQLException;
/**
* Hybrid in-memory and database backed cache built on H2.
@@ -341,8 +340,10 @@
b.put(keyType.get(r, 1));
}
}
- } catch (JdbcSQLException e) {
- if (e.getCause() instanceof InvalidClassException) {
+ } catch (Exception e) {
+ if (Throwables.getCausalChain(e)
+ .stream()
+ .anyMatch(InvalidClassException.class::isInstance)) {
// If deserialization failed using default Java serialization, this means we are using
// the old serialVersionUID-based invalidation strategy. In that case, authors are
// most likely bumping serialVersionUID rather than using the new versioning in the
diff --git a/java/com/google/gerrit/server/change/ChangeEditResource.java b/java/com/google/gerrit/server/change/ChangeEditResource.java
index 392709e..5419ee9 100644
--- a/java/com/google/gerrit/server/change/ChangeEditResource.java
+++ b/java/com/google/gerrit/server/change/ChangeEditResource.java
@@ -32,6 +32,10 @@
public class ChangeEditResource implements RestResource {
public static final TypeLiteral<RestView<ChangeEditResource>> CHANGE_EDIT_KIND =
new TypeLiteral<RestView<ChangeEditResource>>() {};
+ public static final TypeLiteral<RestView<ChangeEditResource.Publish>> CHANGE_EDIT_PUBLISH_KIND =
+ new TypeLiteral<RestView<ChangeEditResource.Publish>>() {};
+ public static final TypeLiteral<RestView<ChangeEditResource.Rebase>> CHANGE_EDIT_REBASE_KIND =
+ new TypeLiteral<RestView<ChangeEditResource.Rebase>>() {};
private final ChangeResource change;
private final ChangeEdit edit;
@@ -60,4 +64,16 @@
public String getPath() {
return path;
}
+
+ public static class Publish extends ChangeEditResource {
+ public Publish(ChangeResource change, ChangeEdit edit, String path) {
+ super(change, edit, path);
+ }
+ }
+
+ public static class Rebase extends ChangeEditResource {
+ public Rebase(ChangeResource change, ChangeEdit edit, String path) {
+ super(change, edit, path);
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/change/PureRevert.java b/java/com/google/gerrit/server/change/PureRevert.java
index 2c450a7..ddc9661 100644
--- a/java/com/google/gerrit/server/change/PureRevert.java
+++ b/java/com/google/gerrit/server/change/PureRevert.java
@@ -28,6 +28,7 @@
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import com.google.inject.Singleton;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
@@ -42,6 +43,7 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+@Singleton
public class PureRevert {
private final MergeUtil.Factory mergeUtilFactory;
private final GitRepositoryManager repoManager;
diff --git a/java/com/google/gerrit/server/config/GitwebConfig.java b/java/com/google/gerrit/server/config/GitwebConfig.java
index f38572d..b5f09fd 100644
--- a/java/com/google/gerrit/server/config/GitwebConfig.java
+++ b/java/com/google/gerrit/server/config/GitwebConfig.java
@@ -36,6 +36,8 @@
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import java.net.MalformedURLException;
+import java.net.URL;
import org.eclipse.jgit.lib.Config;
public class GitwebConfig {
@@ -178,7 +180,11 @@
private final GitwebType type;
@Inject
- GitwebConfig(GitwebCgiConfig cgiConfig, @GerritServerConfig Config cfg) {
+ GitwebConfig(
+ GitwebCgiConfig cgiConfig,
+ @GerritServerConfig Config cfg,
+ @Nullable @CanonicalWebUrl String gerritUrl)
+ throws MalformedURLException {
if (isDisabled(cfg)) {
type = null;
url = null;
@@ -191,7 +197,14 @@
// Use an externally managed gitweb instance, and not an internal one.
url = cfgUrl;
} else {
- url = firstNonNull(cfgUrl, "gitweb");
+ String baseGerritUrl;
+ if (gerritUrl != null) {
+ URL u = new URL(gerritUrl);
+ baseGerritUrl = u.getPath();
+ } else {
+ baseGerritUrl = "/";
+ }
+ url = firstNonNull(cfgUrl, baseGerritUrl + "gitweb");
}
}
}
diff --git a/java/com/google/gerrit/server/index/IndexModule.java b/java/com/google/gerrit/server/index/IndexModule.java
index d957558..3c9538c 100644
--- a/java/com/google/gerrit/server/index/IndexModule.java
+++ b/java/com/google/gerrit/server/index/IndexModule.java
@@ -104,7 +104,7 @@
public IndexModule(
ListeningExecutorService interactiveExecutor, ListeningExecutorService batchExecutor) {
- this.threads = -1;
+ this.threads = 0;
this.interactiveExecutor = interactiveExecutor;
this.batchExecutor = batchExecutor;
this.closeExecutorsOnShutdown = false;
@@ -211,11 +211,12 @@
return interactiveExecutor;
}
int threads = this.threads;
- if (threads <= 0) {
- threads = config.getInt("index", null, "threads", 0);
- }
- if (threads <= 0) {
- threads = Runtime.getRuntime().availableProcessors() / 2 + 1;
+ if (threads < 0) {
+ return MoreExecutors.newDirectExecutorService();
+ } else if (threads == 0) {
+ threads =
+ config.getInt(
+ "index", null, "threads", Runtime.getRuntime().availableProcessors() / 2 + 1);
}
return MoreExecutors.listeningDecorator(
workQueue.createQueue(threads, "Index-Interactive", true));
@@ -230,7 +231,9 @@
return batchExecutor;
}
int threads = config.getInt("index", null, "batchThreads", 0);
- if (threads <= 0) {
+ if (threads < 0) {
+ return MoreExecutors.newDirectExecutorService();
+ } else if (threads == 0) {
threads = Runtime.getRuntime().availableProcessors();
}
return MoreExecutors.listeningDecorator(workQueue.createQueue(threads, "Index-Batch", true));
diff --git a/java/com/google/gerrit/server/index/change/StalenessChecker.java b/java/com/google/gerrit/server/index/change/StalenessChecker.java
index e567d77..642385c 100644
--- a/java/com/google/gerrit/server/index/change/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/change/StalenessChecker.java
@@ -34,6 +34,7 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.RefState;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -103,6 +104,7 @@
parsePatterns(cd));
}
+ @UsedAt(UsedAt.Project.GOOGLE)
public static boolean isStale(
GitRepositoryManager repoManager,
Change.Id id,
diff --git a/java/com/google/gerrit/server/mail/send/EmailArguments.java b/java/com/google/gerrit/server/mail/send/EmailArguments.java
index 04f4d6c..f49951f 100644
--- a/java/com/google/gerrit/server/mail/send/EmailArguments.java
+++ b/java/com/google/gerrit/server/mail/send/EmailArguments.java
@@ -22,6 +22,7 @@
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.IdentifiedUser.GenericFactory;
+import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.AllProjectsName;
@@ -49,6 +50,7 @@
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
+@UsedAt(UsedAt.Project.PLUGINS_ALL)
public class EmailArguments {
final GitRepositoryManager server;
final ProjectCache projectCache;
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
index 3653bc7..010c5c0 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
@@ -120,9 +120,7 @@
ChangeNoteUtil noteUtil, PersonIdent serverIdent, CurrentUser u, Date when) {
checkUserType(u);
if (u instanceof IdentifiedUser) {
- return noteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(u.asIdentifiedUser().getAccount(), when, serverIdent);
+ return noteUtil.newIdent(u.asIdentifiedUser().getAccount(), when, serverIdent);
} else if (u instanceof InternalUser) {
return serverIdent;
}
@@ -177,7 +175,7 @@
}
protected PersonIdent newIdent(Account.Id authorId, Date when) {
- return noteUtil.getLegacyChangeNoteWrite().newIdent(authorId, when, serverIdent);
+ return noteUtil.newIdent(authorId, when, serverIdent);
}
/** Whether no updates have been done. */
diff --git a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
index 6b4bea7..169fd2e 100644
--- a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
@@ -178,12 +178,7 @@
for (Map.Entry<RevId, RevisionNoteBuilder> e : builders.entrySet()) {
updatedRevs.add(e.getKey());
ObjectId id = ObjectId.fromString(e.getKey().get());
- byte[] data =
- e.getValue()
- .build(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteWrite(),
- noteUtil.getChangeNoteJson().getWriteJson());
+ byte[] data = e.getValue().build(noteUtil.getChangeNoteJson());
if (!Arrays.equals(data, e.getValue().baseRaw)) {
touchedAnyRevs = true;
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNoteJson.java b/java/com/google/gerrit/server/notedb/ChangeNoteJson.java
index 0475fe3..483b2e9 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNoteJson.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNoteJson.java
@@ -14,18 +14,14 @@
package com.google.gerrit.server.notedb;
-import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.Timestamp;
-import org.eclipse.jgit.lib.Config;
@Singleton
public class ChangeNoteJson {
private final Gson gson = newGson();
- private final boolean writeJson;
static Gson newGson() {
return new GsonBuilder()
@@ -37,13 +33,4 @@
public Gson getGson() {
return gson;
}
-
- public boolean getWriteJson() {
- return writeJson;
- }
-
- @Inject
- ChangeNoteJson(@GerritServerConfig Config config) {
- this.writeJson = config.getBoolean("notedb", "writeJson", true);
- }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index 070f974..cf86c99 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -14,7 +14,15 @@
package com.google.gerrit.server.notedb;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.config.GerritServerId;
import com.google.inject.Inject;
+import java.util.Date;
+import java.util.Optional;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.FooterKey;
public class ChangeNoteUtil {
@@ -56,17 +64,21 @@
static final String TAG = FOOTER_TAG.getName();
private final LegacyChangeNoteRead legacyChangeNoteRead;
- private final LegacyChangeNoteWrite legacyChangeNoteWrite;
private final ChangeNoteJson changeNoteJson;
+ private final AccountCache accountCache;
+ private final String serverId;
+
@Inject
public ChangeNoteUtil(
ChangeNoteJson changeNoteJson,
LegacyChangeNoteRead legacyChangeNoteRead,
- LegacyChangeNoteWrite legacyChangeNoteWrite) {
+ AccountCache accountCache,
+ @GerritServerId String serverId) {
+ this.accountCache = accountCache;
+ this.serverId = serverId;
this.changeNoteJson = changeNoteJson;
this.legacyChangeNoteRead = legacyChangeNoteRead;
- this.legacyChangeNoteWrite = legacyChangeNoteWrite;
}
public LegacyChangeNoteRead getLegacyChangeNoteRead() {
@@ -77,7 +89,18 @@
return changeNoteJson;
}
- public LegacyChangeNoteWrite getLegacyChangeNoteWrite() {
- return legacyChangeNoteWrite;
+ public PersonIdent newIdent(Account.Id authorId, Date when, PersonIdent serverIdent) {
+ Optional<Account> author = accountCache.get(authorId).map(AccountState::getAccount);
+ return new PersonIdent(
+ author.map(Account::getName).orElseGet(() -> Account.getName(authorId)),
+ authorId.get() + "@" + serverId,
+ when,
+ serverIdent.getTimeZone());
+ }
+
+ @VisibleForTesting
+ public PersonIdent newIdent(Account author, Date when, PersonIdent serverIdent) {
+ return new PersonIdent(
+ author.getName(), author.getId().get() + "@" + serverId, when, serverIdent.getTimeZone());
}
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
index 894e979..66dd5e8 100644
--- a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
+++ b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
@@ -88,7 +88,7 @@
return comments;
}
- private static boolean isJson(byte[] raw, int offset) {
+ static boolean isJson(byte[] raw, int offset) {
return raw[offset] == '{' || raw[offset] == '[';
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 445f7a0..69fbfe1 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -402,6 +402,7 @@
if (notes != null) {
draftUpdate = draftUpdateFactory.create(notes, accountId, realAccountId, authorIdent, when);
} else {
+ // tests will always take the notes != null path above.
draftUpdate =
draftUpdateFactory.create(getChange(), accountId, realAccountId, authorIdent, when);
}
@@ -526,14 +527,7 @@
checkComments(rnm.revisionNotes, builders);
for (Map.Entry<RevId, RevisionNoteBuilder> e : builders.entrySet()) {
- ObjectId data =
- inserter.insert(
- OBJ_BLOB,
- e.getValue()
- .build(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteWrite(),
- noteUtil.getChangeNoteJson().getWriteJson()));
+ ObjectId data = inserter.insert(OBJ_BLOB, e.getValue().build(noteUtil.getChangeNoteJson()));
rnm.noteMap.set(ObjectId.fromString(e.getKey().get()), data);
}
diff --git a/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java b/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java
new file mode 100644
index 0000000..9a9a211
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java
@@ -0,0 +1,190 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+import static com.google.gerrit.server.notedb.RevisionNote.MAX_NOTE_SZ;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.server.config.GerritServerIdProvider;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.notes.Note;
+import org.eclipse.jgit.notes.NoteMap;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.util.MutableInteger;
+
+@Singleton
+public class CommentJsonMigrator {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final LegacyChangeNoteRead legacyChangeNoteRead;
+ private final ChangeNoteJson changeNoteJson;
+
+ @Inject
+ CommentJsonMigrator(
+ ChangeNoteJson changeNoteJson, GerritServerIdProvider gerritServerIdProvider) {
+ this.changeNoteJson = changeNoteJson;
+ this.legacyChangeNoteRead = new LegacyChangeNoteRead(gerritServerIdProvider.get());
+ }
+
+ CommentJsonMigrator(ChangeNoteJson changeNoteJson, String serverId) {
+ this.changeNoteJson = changeNoteJson;
+ this.legacyChangeNoteRead = new LegacyChangeNoteRead(serverId);
+ }
+
+ public boolean migrateChanges(
+ Project.NameKey project, Repository repo, RevWalk rw, ObjectInserter ins, BatchRefUpdate bru)
+ throws IOException {
+ boolean ok = true;
+ for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) {
+ Change.Id changeId = Change.Id.fromRef(ref.getName());
+ if (changeId == null || !ref.getName().equals(RefNames.changeMetaRef(changeId))) {
+ continue;
+ }
+ ok &= migrateOne(project, rw, ins, bru, Status.PUBLISHED, changeId, ref);
+ }
+ return ok;
+ }
+
+ public boolean migrateDrafts(
+ Project.NameKey allUsers,
+ Repository allUsersRepo,
+ RevWalk rw,
+ ObjectInserter ins,
+ BatchRefUpdate bru)
+ throws IOException {
+ boolean ok = true;
+ for (Ref ref : allUsersRepo.getRefDatabase().getRefsByPrefix(RefNames.REFS_DRAFT_COMMENTS)) {
+ Change.Id changeId = Change.Id.fromAllUsersRef(ref.getName());
+ if (changeId == null) {
+ continue;
+ }
+ ok &= migrateOne(allUsers, rw, ins, bru, Status.DRAFT, changeId, ref);
+ }
+ return ok;
+ }
+
+ private boolean migrateOne(
+ Project.NameKey project,
+ RevWalk rw,
+ ObjectInserter ins,
+ BatchRefUpdate bru,
+ Status status,
+ Change.Id changeId,
+ Ref ref) {
+ ObjectId oldId = ref.getObjectId();
+ try {
+ if (!hasAnyLegacyComments(rw, oldId)) {
+ return true;
+ }
+ } catch (IOException e) {
+ logger.atInfo().log(
+ String.format(
+ "Error reading change %s in %s; attempting migration anyway", changeId, project),
+ e);
+ }
+
+ try {
+ reset(rw, oldId);
+
+ ObjectReader reader = rw.getObjectReader();
+ ObjectId newId = null;
+ RevCommit c;
+ while ((c = rw.next()) != null) {
+ CommitBuilder cb = new CommitBuilder();
+ cb.setAuthor(c.getAuthorIdent());
+ cb.setCommitter(c.getCommitterIdent());
+ cb.setMessage(c.getFullMessage());
+ cb.setEncoding(c.getEncoding());
+ if (newId != null) {
+ cb.setParentId(newId);
+ }
+
+ // Read/write using the low-level RevisionNote API, which works regardless of NotesMigration
+ // state.
+ NoteMap noteMap = NoteMap.read(reader, c);
+ RevisionNoteMap<ChangeRevisionNote> revNoteMap =
+ RevisionNoteMap.parse(
+ changeNoteJson, legacyChangeNoteRead, changeId, reader, noteMap, status);
+ RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(revNoteMap);
+
+ for (RevId revId : revNoteMap.revisionNotes.keySet()) {
+ // Call cache.get on each known RevId to read the old note in whichever format, then write
+ // the note in JSON format.
+ byte[] data = cache.get(revId).build(changeNoteJson);
+ noteMap.set(ObjectId.fromString(revId.get()), ins.insert(OBJ_BLOB, data));
+ }
+ cb.setTreeId(noteMap.writeTree(ins));
+ newId = ins.insert(cb);
+ }
+
+ bru.addCommand(new ReceiveCommand(oldId, newId, ref.getName()));
+ return true;
+ } catch (ConfigInvalidException | IOException e) {
+ logger.atInfo().log(String.format("Error migrating change %s in %s", changeId, project), e);
+ return false;
+ }
+ }
+
+ private static boolean hasAnyLegacyComments(RevWalk rw, ObjectId id) throws IOException {
+ ObjectReader reader = rw.getObjectReader();
+ reset(rw, id);
+
+ // Check the note map at each commit, not just the tip. It's possible that the server switched
+ // from legacy to JSON partway through its history, which would have mixed legacy/JSON comments
+ // in its history. Although the tip commit would continue to parse once we remove the legacy
+ // parser, our goal is really to expunge all vestiges of the old format, which implies rewriting
+ // history (and thus returning true) in this case.
+ RevCommit c;
+ while ((c = rw.next()) != null) {
+ NoteMap noteMap = NoteMap.read(reader, c);
+ for (Note note : noteMap) {
+ // Match pre-parsing logic in RevisionNote#parse().
+ byte[] raw = reader.open(note.getData(), OBJ_BLOB).getCachedBytes(MAX_NOTE_SZ);
+ MutableInteger p = new MutableInteger();
+ RevisionNote.trimLeadingEmptyLines(raw, p);
+ if (!ChangeRevisionNote.isJson(raw, p.value)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static void reset(RevWalk rw, ObjectId id) throws IOException {
+ rw.reset();
+ rw.sort(RevSort.TOPO);
+ rw.sort(RevSort.REVERSE);
+ rw.markStart(rw.parseCommit(id));
+ }
+}
diff --git a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
index 9a8c130..0cd3452 100644
--- a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
+++ b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
@@ -239,13 +239,7 @@
Map<RevId, RevisionNoteBuilder> builders = cache.getBuilders();
for (Map.Entry<RevId, RevisionNoteBuilder> entry : builders.entrySet()) {
ObjectId objectId = ObjectId.fromString(entry.getKey().get());
- byte[] data =
- entry
- .getValue()
- .build(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteWrite(),
- noteUtil.getChangeNoteJson().getWriteJson());
+ byte[] data = entry.getValue().build(noteUtil.getChangeNoteJson());
if (data.length == 0) {
revNotesMap.noteMap.remove(objectId);
} else {
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
index 8bf286d..b8c7d7d 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
@@ -82,19 +82,13 @@
delete = new HashSet<>();
}
- public byte[] build(ChangeNoteUtil noteUtil, boolean writeJson) throws IOException {
- return build(noteUtil.getChangeNoteJson(), noteUtil.getLegacyChangeNoteWrite(), writeJson);
+ public byte[] build(ChangeNoteUtil noteUtil) throws IOException {
+ return build(noteUtil.getChangeNoteJson());
}
- public byte[] build(
- ChangeNoteJson changeNoteJson, LegacyChangeNoteWrite legacyChangeNoteWrite, boolean writeJson)
- throws IOException {
+ public byte[] build(ChangeNoteJson changeNoteJson) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- if (writeJson) {
- buildNoteJson(changeNoteJson, out);
- } else {
- buildNoteLegacy(legacyChangeNoteWrite, out);
- }
+ buildNoteJson(changeNoteJson, out);
return out.toByteArray();
}
@@ -142,22 +136,4 @@
noteUtil.getGson().toJson(data, osw);
}
}
-
- private void buildNoteLegacy(LegacyChangeNoteWrite noteUtil, OutputStream out)
- throws IOException {
- if (pushCert != null) {
- byte[] certBytes = pushCert.getBytes(UTF_8);
- out.write(certBytes, 0, trimTrailingNewlines(certBytes));
- out.write('\n');
- }
- noteUtil.buildNote(buildCommentMap(), out);
- }
-
- private static int trimTrailingNewlines(byte[] bytes) {
- int p = bytes.length;
- while (p > 1 && bytes[p - 1] == '\n') {
- p--;
- }
- return p;
- }
}
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
index 3a0d595..e7ac5b7 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
@@ -142,7 +142,7 @@
for (Map.Entry<RevId, RevisionNoteBuilder> e : builders.entrySet()) {
updatedRevs.add(e.getKey());
ObjectId id = ObjectId.fromString(e.getKey().get());
- byte[] data = e.getValue().build(noteUtil, true);
+ byte[] data = e.getValue().build(noteUtil);
if (!Arrays.equals(data, e.getValue().baseRaw)) {
touchedAnyRevs = true;
}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java b/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
index 5d2ce97..ab2203e 100644
--- a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
+++ b/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
@@ -546,7 +546,7 @@
if (id == null) {
return new PersonIdent(serverIdent, events.getWhen());
}
- return changeNoteUtil.getLegacyChangeNoteWrite().newIdent(id, events.getWhen(), serverIdent);
+ return changeNoteUtil.newIdent(id, events.getWhen(), serverIdent);
}
private List<HashtagsEvent> getHashtagsEvents(Change change, NoteDbUpdateManager manager)
diff --git a/java/com/google/gerrit/server/patch/AutoMerger.java b/java/com/google/gerrit/server/patch/AutoMerger.java
index f1b6639..15fedd7 100644
--- a/java/com/google/gerrit/server/patch/AutoMerger.java
+++ b/java/com/google/gerrit/server/patch/AutoMerger.java
@@ -21,6 +21,7 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.InMemoryInserter;
import com.google.inject.Inject;
@@ -54,6 +55,7 @@
public class AutoMerger {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ @UsedAt(UsedAt.Project.GOOGLE)
public static boolean cacheAutomerge(Config cfg) {
return cfg.getBoolean("change", null, "cacheAutomerge", true);
}
diff --git a/java/com/google/gerrit/server/project/AccountsSection.java b/java/com/google/gerrit/server/project/AccountsSection.java
index 087a314a..30bd244 100644
--- a/java/com/google/gerrit/server/project/AccountsSection.java
+++ b/java/com/google/gerrit/server/project/AccountsSection.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.project;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.PermissionRule;
import java.util.ArrayList;
import java.util.List;
@@ -21,14 +22,14 @@
public class AccountsSection {
protected List<PermissionRule> sameGroupVisibility;
- public List<PermissionRule> getSameGroupVisibility() {
+ public ImmutableList<PermissionRule> getSameGroupVisibility() {
if (sameGroupVisibility == null) {
- sameGroupVisibility = new ArrayList<>();
+ sameGroupVisibility = ImmutableList.of();
}
- return sameGroupVisibility;
+ return ImmutableList.copyOf(sameGroupVisibility);
}
public void setSameGroupVisibility(List<PermissionRule> sameGroupVisibility) {
- this.sameGroupVisibility = sameGroupVisibility;
+ this.sameGroupVisibility = new ArrayList<>(sameGroupVisibility);
}
}
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 1796c40..d42b652 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -737,7 +737,7 @@
}
}
- private List<PermissionRule> loadPermissionRules(
+ private ImmutableList<PermissionRule> loadPermissionRules(
Config rc,
String section,
String subsection,
@@ -746,7 +746,7 @@
boolean useRange) {
Permission perm = new Permission(varName);
loadPermissionRules(rc, section, subsection, varName, groupsByName, perm, useRange);
- return perm.getRules();
+ return ImmutableList.copyOf(perm.getRules());
}
private void loadPermissionRules(
diff --git a/java/com/google/gerrit/server/restapi/account/Module.java b/java/com/google/gerrit/server/restapi/account/Module.java
index 7b09bc9..f37687f 100644
--- a/java/com/google/gerrit/server/restapi/account/Module.java
+++ b/java/com/google/gerrit/server/restapi/account/Module.java
@@ -107,6 +107,8 @@
get(ACCOUNT_KIND, "external.ids").to(GetExternalIds.class);
post(ACCOUNT_KIND, "external.ids:delete").to(DeleteExternalIds.class);
+ // The gpgkeys REST endpoints are bound via GpgApiModule.
+
factory(AccountsUpdate.Factory.class);
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
index e42e5d1..4bbd8fc 100644
--- a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
+++ b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
@@ -25,6 +25,7 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountsUpdate;
@@ -119,6 +120,7 @@
return Strings.isNullOrEmpty(newPassword) ? Response.<String>none() : Response.ok(newPassword);
}
+ @UsedAt(UsedAt.Project.PLUGIN_SERVICEUSER)
public static String generate() {
byte[] rand = new byte[LEN];
rng.nextBytes(rand);
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index ae77060..826c792 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -120,9 +120,10 @@
}
/**
- * Create handler that is activated when collection element is accessed but doesn't exist, e. g.
- * PUT request with a path was called but change edit wasn't created yet. Change edit is created
- * and PUT handler is called.
+ * This method is invoked if a DELETE request on a non-existing member is done. For change edits
+ * this is the case if a DELETE request for a file in a change edit is done and the change edit
+ * doesn't exist yet (and hence the parse method returned ResourceNotFoundException). In this case
+ * we want to create the change edit on the fly and delete the file with the given id in it.
*/
@Override
public DeleteFile delete(ChangeResource parent, IdString id) throws RestApiException {
@@ -132,6 +133,11 @@
return deleteFileFactory.create(id.get());
}
+ /**
+ * Create handler that is activated when collection element is accessed but doesn't exist, e. g.
+ * PUT request with a path was called but change edit wasn't created yet. Change edit is created
+ * and PUT handler is called.
+ */
public static class Create
implements RestCreateView<ChangeResource, ChangeEditResource, Put.Input> {
private final Put putEdit;
diff --git a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
index 42675f6..75019af 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
@@ -30,13 +30,15 @@
public class GetPureRevert implements RestReadView<ChangeResource> {
private final PureRevert pureRevert;
+ @Nullable private String claimedOriginal;
@Option(
name = "--claimed-original",
aliases = {"-o"},
usage = "SHA1 (40 digit hex) of the original commit")
- @Nullable
- private String claimedOriginal;
+ public void setClaimedOriginal(String claimedOriginal) {
+ this.claimedOriginal = claimedOriginal;
+ }
@Inject
GetPureRevert(PureRevert pureRevert) {
diff --git a/java/com/google/gerrit/server/restapi/change/Module.java b/java/com/google/gerrit/server/restapi/change/Module.java
index d27b136..ef0ba2d 100644
--- a/java/com/google/gerrit/server/restapi/change/Module.java
+++ b/java/com/google/gerrit/server/restapi/change/Module.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.server.change.ChangeEditResource.CHANGE_EDIT_KIND;
+import static com.google.gerrit.server.change.ChangeEditResource.CHANGE_EDIT_PUBLISH_KIND;
+import static com.google.gerrit.server.change.ChangeEditResource.CHANGE_EDIT_REBASE_KIND;
import static com.google.gerrit.server.change.ChangeMessageResource.CHANGE_MESSAGE_KIND;
import static com.google.gerrit.server.change.ChangeResource.CHANGE_KIND;
import static com.google.gerrit.server.change.CommentResource.COMMENT_KIND;
@@ -65,6 +67,8 @@
DynamicMap.mapOf(binder(), REVIEWER_KIND);
DynamicMap.mapOf(binder(), REVISION_KIND);
DynamicMap.mapOf(binder(), CHANGE_EDIT_KIND);
+ DynamicMap.mapOf(binder(), CHANGE_EDIT_PUBLISH_KIND);
+ DynamicMap.mapOf(binder(), CHANGE_EDIT_REBASE_KIND);
DynamicMap.mapOf(binder(), VOTE_KIND);
DynamicMap.mapOf(binder(), CHANGE_MESSAGE_KIND);
diff --git a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
index b356f18..36a0561 100644
--- a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
@@ -19,8 +19,8 @@
import com.google.gerrit.extensions.restapi.AcceptsPost;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
@@ -44,28 +44,32 @@
@Singleton
public class PublishChangeEdit
- implements ChildCollection<ChangeResource, ChangeEditResource>, AcceptsPost<ChangeResource> {
+ implements ChildCollection<ChangeResource, ChangeEditResource.Publish>,
+ AcceptsPost<ChangeResource> {
+ private final DynamicMap<RestView<ChangeEditResource.Publish>> views;
private final Publish publish;
@Inject
- PublishChangeEdit(Publish publish) {
+ PublishChangeEdit(DynamicMap<RestView<ChangeEditResource.Publish>> views, Publish publish) {
+ this.views = views;
this.publish = publish;
}
@Override
- public DynamicMap<RestView<ChangeEditResource>> views() {
- throw new NotImplementedException();
+ public DynamicMap<RestView<ChangeEditResource.Publish>> views() {
+ return views;
}
@Override
- public RestView<ChangeResource> list() {
- throw new NotImplementedException();
+ public RestView<ChangeResource> list() throws ResourceNotFoundException {
+ throw new ResourceNotFoundException();
}
@Override
- public ChangeEditResource parse(ChangeResource parent, IdString id) {
- throw new NotImplementedException();
+ public ChangeEditResource.Publish parse(ChangeResource parent, IdString id)
+ throws ResourceNotFoundException {
+ throw new ResourceNotFoundException();
}
@Override
diff --git a/java/com/google/gerrit/server/restapi/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
index b6fc010..b7be2a8 100644
--- a/java/com/google/gerrit/server/restapi/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -89,7 +89,10 @@
throw new UnprocessableEntityException(input.assignee + " is not active");
}
try {
- rsrc.permissions().database(db).user(assignee).check(ChangePermission.READ);
+ rsrc.permissions()
+ .database(db)
+ .absentUser(assignee.getAccountId())
+ .check(ChangePermission.READ);
} catch (AuthException e) {
throw new AuthException("read not permitted for " + input.assignee);
}
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
index 7e1bb4d..47af894 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
@@ -20,11 +20,10 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.ChangeEditResource;
@@ -33,6 +32,9 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -41,28 +43,32 @@
@Singleton
public class RebaseChangeEdit
- implements ChildCollection<ChangeResource, ChangeEditResource>, AcceptsPost<ChangeResource> {
+ implements ChildCollection<ChangeResource, ChangeEditResource.Rebase>,
+ AcceptsPost<ChangeResource> {
+ private final DynamicMap<RestView<ChangeEditResource.Rebase>> views;
private final Rebase rebase;
@Inject
- RebaseChangeEdit(Rebase rebase) {
+ RebaseChangeEdit(DynamicMap<RestView<ChangeEditResource.Rebase>> views, Rebase rebase) {
+ this.views = views;
this.rebase = rebase;
}
@Override
- public DynamicMap<RestView<ChangeEditResource>> views() {
- throw new NotImplementedException();
+ public DynamicMap<RestView<ChangeEditResource.Rebase>> views() {
+ return views;
}
@Override
- public RestView<ChangeResource> list() {
- throw new NotImplementedException();
+ public RestView<ChangeResource> list() throws ResourceNotFoundException {
+ throw new ResourceNotFoundException();
}
@Override
- public ChangeEditResource parse(ChangeResource parent, IdString id) {
- throw new NotImplementedException();
+ public ChangeEditResource.Rebase parse(ChangeResource parent, IdString id)
+ throws ResourceNotFoundException {
+ throw new ResourceNotFoundException();
}
@Override
@@ -71,19 +77,23 @@
}
@Singleton
- public static class Rebase implements RestModifyView<ChangeResource, Input> {
-
+ public static class Rebase extends RetryingRestModifyView<ChangeResource, Input, Response<?>> {
private final GitRepositoryManager repositoryManager;
private final ChangeEditModifier editModifier;
@Inject
- Rebase(GitRepositoryManager repositoryManager, ChangeEditModifier editModifier) {
+ Rebase(
+ RetryHelper retryHelper,
+ GitRepositoryManager repositoryManager,
+ ChangeEditModifier editModifier) {
+ super(retryHelper);
this.repositoryManager = repositoryManager;
this.editModifier = editModifier;
}
@Override
- public Response<?> apply(ChangeResource rsrc, Input in)
+ protected Response<?> applyImpl(
+ BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input in)
throws AuthException, ResourceConflictException, IOException, OrmException,
PermissionBackendException {
Project.NameKey project = rsrc.getProject();
diff --git a/java/com/google/gerrit/server/restapi/project/Index.java b/java/com/google/gerrit/server/restapi/project/Index.java
index 24f32f6..5547864 100644
--- a/java/com/google/gerrit/server/restapi/project/Index.java
+++ b/java/com/google/gerrit/server/restapi/project/Index.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Android Open Source Project
+// 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.
@@ -16,57 +16,73 @@
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
-import com.google.common.io.ByteStreams;
+import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
-import com.google.gerrit.extensions.api.projects.ProjectInput;
+import com.google.gerrit.extensions.api.projects.IndexProjectInput;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.git.MultiProgressMonitor;
-import com.google.gerrit.server.git.MultiProgressMonitor.Task;
import com.google.gerrit.server.index.IndexExecutor;
-import com.google.gerrit.server.index.change.AllChangesIndexer;
-import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
+import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.io.IOException;
import java.util.concurrent.Future;
-import org.eclipse.jgit.util.io.NullOutputStream;
-@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+@RequiresCapability(GlobalCapability.MAINTAIN_SERVER)
@Singleton
-public class Index implements RestModifyView<ProjectResource, ProjectInput> {
+public class Index implements RestModifyView<ProjectResource, IndexProjectInput> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<AllChangesIndexer> allChangesIndexerProvider;
- private final ChangeIndexer indexer;
+ private final ProjectIndexer indexer;
private final ListeningExecutorService executor;
+ private final Provider<ListChildProjects> listChildProjectsProvider;
@Inject
Index(
- Provider<AllChangesIndexer> allChangesIndexerProvider,
- ChangeIndexer indexer,
- @IndexExecutor(BATCH) ListeningExecutorService executor) {
- this.allChangesIndexerProvider = allChangesIndexerProvider;
+ ProjectIndexer indexer,
+ @IndexExecutor(BATCH) ListeningExecutorService executor,
+ Provider<ListChildProjects> listChildProjectsProvider) {
this.indexer = indexer;
this.executor = executor;
+ this.listChildProjectsProvider = listChildProjectsProvider;
}
@Override
- public Response.Accepted apply(ProjectResource resource, ProjectInput input) {
- Project.NameKey project = resource.getNameKey();
- Task mpt =
- new MultiProgressMonitor(ByteStreams.nullOutputStream(), "Reindexing project")
- .beginSubTask("", MultiProgressMonitor.UNKNOWN);
- AllChangesIndexer allChangesIndexer = allChangesIndexerProvider.get();
- allChangesIndexer.setVerboseOut(NullOutputStream.INSTANCE);
- // The REST call is just a trigger for async reindexing, so it is safe to ignore the future's
- // return value.
+ public Response.Accepted apply(ProjectResource rsrc, IndexProjectInput input)
+ throws IOException, AuthException, OrmException, PermissionBackendException,
+ ResourceConflictException {
+ String response = "Project " + rsrc.getName() + " submitted for reindexing";
+
+ reindexAsync(rsrc.getNameKey());
+ if (Boolean.TRUE.equals(input.indexChildren)) {
+ ListChildProjects listChildProjects = listChildProjectsProvider.get();
+ listChildProjects.setRecursive(true);
+ listChildProjects.apply(rsrc).forEach(p -> reindexAsync(new Project.NameKey(p.name)));
+
+ response += " (indexing children recursively)";
+ }
+ return Response.accepted(response);
+ }
+
+ private void reindexAsync(Project.NameKey project) {
@SuppressWarnings("unused")
- Future<Void> ignored =
- executor.submit(allChangesIndexer.reindexProject(indexer, project, mpt, mpt));
- return Response.accepted("Project " + project + " submitted for reindexing");
+ Future<?> possiblyIgnoredError =
+ executor.submit(
+ () -> {
+ try {
+ indexer.index(project);
+ } catch (IOException e) {
+ logger.atWarning().withCause(e).log("reindexing project %s failed", project);
+ }
+ });
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/IndexChanges.java b/java/com/google/gerrit/server/restapi/project/IndexChanges.java
new file mode 100644
index 0000000..b84f86c
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/project/IndexChanges.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.restapi.project;
+
+import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.api.projects.ProjectInput;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.git.MultiProgressMonitor;
+import com.google.gerrit.server.git.MultiProgressMonitor.Task;
+import com.google.gerrit.server.index.IndexExecutor;
+import com.google.gerrit.server.index.change.AllChangesIndexer;
+import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.util.concurrent.Future;
+import org.eclipse.jgit.util.io.NullOutputStream;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+@Singleton
+public class IndexChanges implements RestModifyView<ProjectResource, ProjectInput> {
+
+ private final Provider<AllChangesIndexer> allChangesIndexerProvider;
+ private final ChangeIndexer indexer;
+ private final ListeningExecutorService executor;
+
+ @Inject
+ IndexChanges(
+ Provider<AllChangesIndexer> allChangesIndexerProvider,
+ ChangeIndexer indexer,
+ @IndexExecutor(BATCH) ListeningExecutorService executor) {
+ this.allChangesIndexerProvider = allChangesIndexerProvider;
+ this.indexer = indexer;
+ this.executor = executor;
+ }
+
+ @Override
+ public Response.Accepted apply(ProjectResource resource, ProjectInput input) {
+ Project.NameKey project = resource.getNameKey();
+ Task mpt =
+ new MultiProgressMonitor(ByteStreams.nullOutputStream(), "Reindexing project")
+ .beginSubTask("", MultiProgressMonitor.UNKNOWN);
+ AllChangesIndexer allChangesIndexer = allChangesIndexerProvider.get();
+ allChangesIndexer.setVerboseOut(NullOutputStream.INSTANCE);
+ // The REST call is just a trigger for async reindexing, so it is safe to ignore the future's
+ // return value.
+ @SuppressWarnings("unused")
+ Future<?> possiblyIgnoredError =
+ executor.submit(allChangesIndexer.reindexProject(indexer, project, mpt, mpt));
+ return Response.accepted("Project " + project + " submitted for reindexing");
+ }
+}
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java
index d2e59cd..f2047e0 100644
--- a/java/com/google/gerrit/server/restapi/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -68,6 +68,7 @@
get(PROJECT_KIND, "statistics.git").to(GetStatistics.class);
post(PROJECT_KIND, "gc").to(GarbageCollect.class);
post(PROJECT_KIND, "index").to(Index.class);
+ post(PROJECT_KIND, "index.changes").to(IndexChanges.class);
child(PROJECT_KIND, "branches").to(BranchesCollection.class);
create(BRANCH_KIND).to(CreateBranch.class);
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
index 422c749..c5c8cf0 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
@@ -115,7 +115,7 @@
}
p.add(r);
}
- accessSection.getPermissions().add(p);
+ accessSection.addPermission(p);
}
sections.add(accessSection);
}
diff --git a/java/com/google/gerrit/server/rules/PrologEnvironment.java b/java/com/google/gerrit/server/rules/PrologEnvironment.java
index 083898b..412e0f9 100644
--- a/java/com/google/gerrit/server/rules/PrologEnvironment.java
+++ b/java/com/google/gerrit/server/rules/PrologEnvironment.java
@@ -18,6 +18,7 @@
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache;
@@ -176,6 +177,7 @@
private final int reductionLimit;
private final int compileLimit;
private final PatchSetUtil patchsetUtil;
+ private Emails emails;
@Inject
Args(
@@ -187,7 +189,8 @@
IdentifiedUser.GenericFactory userFactory,
Provider<AnonymousUser> anonymousUser,
@GerritServerConfig Config config,
- PatchSetUtil patchsetUtil) {
+ PatchSetUtil patchsetUtil,
+ Emails emails) {
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.repositoryManager = repositoryManager;
@@ -196,6 +199,7 @@
this.userFactory = userFactory;
this.anonymousUser = anonymousUser;
this.patchsetUtil = patchsetUtil;
+ this.emails = emails;
int limit = config.getInt("rules", null, "reductionLimit", 100000);
reductionLimit = limit <= 0 ? Integer.MAX_VALUE : limit;
@@ -247,5 +251,9 @@
public PatchSetUtil getPatchsetUtil() {
return patchsetUtil;
}
+
+ public Emails getEmails() {
+ return emails;
+ }
}
}
diff --git a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
index 8e39b50..e61fc90 100644
--- a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
+++ b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
@@ -297,7 +297,8 @@
try {
return LabelType.checkName(name);
} catch (IllegalArgumentException e) {
- return LabelType.checkName("Invalid-Prolog-Rules-Label-Name--" + sanitizeLabelName(name));
+ String newName = "Invalid-Prolog-Rules-Label-Name-" + sanitizeLabelName(name);
+ return LabelType.checkName(newName.replace("--", "-"));
}
}
diff --git a/java/com/google/gerrit/server/rules/StoredValues.java b/java/com/google/gerrit/server/rules/StoredValues.java
index 6770732..8b9cfe3 100644
--- a/java/com/google/gerrit/server/rules/StoredValues.java
+++ b/java/com/google/gerrit/server/rules/StoredValues.java
@@ -20,7 +20,6 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
@@ -34,8 +33,6 @@
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListKey;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
@@ -47,6 +44,7 @@
import java.util.Map;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
public final class StoredValues {
public static final StoredValue<Accounts> ACCOUNTS = create(Accounts.class);
@@ -74,32 +72,16 @@
}
}
- public static final StoredValue<PatchSetInfo> PATCH_SET_INFO =
- new StoredValue<PatchSetInfo>() {
+ public static final StoredValue<RevCommit> COMMIT =
+ new StoredValue<RevCommit>() {
@Override
- public PatchSetInfo createValue(Prolog engine) {
- Change change = getChange(engine);
- PatchSet ps = getPatchSet(engine);
- PrologEnvironment env = (PrologEnvironment) engine.control;
- PatchSetInfoFactory patchInfoFactory = env.getArgs().getPatchSetInfoFactory();
- try {
- return patchInfoFactory.get(change.getProject(), ps);
- } catch (PatchSetInfoNotAvailableException e) {
- throw new SystemException(e.getMessage());
- }
- }
- };
-
- public static final StoredValue<String> COMMIT_MESSAGE =
- new StoredValue<String>() {
- @Override
- public String createValue(Prolog engine) {
+ public RevCommit createValue(Prolog engine) {
Change change = getChange(engine);
PatchSet ps = getPatchSet(engine);
PrologEnvironment env = (PrologEnvironment) engine.control;
PatchSetUtil patchSetUtil = env.getArgs().getPatchsetUtil();
try {
- return patchSetUtil.getFullCommitMessage(change.getProject(), ps);
+ return patchSetUtil.getRevCommit(change.getProject(), ps);
} catch (IOException e) {
throw new SystemException(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/schema/JdbcUtil.java b/java/com/google/gerrit/server/schema/JdbcUtil.java
index 2624923..dddf23a 100644
--- a/java/com/google/gerrit/server/schema/JdbcUtil.java
+++ b/java/com/google/gerrit/server/schema/JdbcUtil.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.server.UsedAt;
+
public class JdbcUtil {
public static String hostname(String hostname) {
@@ -26,6 +28,7 @@
return hostname;
}
+ @UsedAt(UsedAt.Project.PLUGINS_ALL)
public static String port(String port) {
if (port != null && !port.isEmpty()) {
return ":" + port;
diff --git a/java/com/google/gerrit/server/schema/SchemaVersion.java b/java/com/google/gerrit/server/schema/SchemaVersion.java
index e8a59d0..61e9c92 100644
--- a/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -19,6 +19,7 @@
import com.google.common.collect.Lists;
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.UsedAt;
import com.google.gwtorm.jdbc.JdbcExecutor;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.gwtorm.server.OrmException;
@@ -35,7 +36,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_168> C = Schema_168.class;
+ public static final Class<Schema_169> C = Schema_169.class;
public static int getBinaryVersion() {
return guessVersion(C);
@@ -49,6 +50,7 @@
this.versionNbr = guessVersion(getClass());
}
+ @UsedAt(UsedAt.Project.PLUGIN_DELETE_PROJECT)
public static int guessVersion(Class<?> c) {
String n = c.getName();
n = n.substring(n.lastIndexOf('_') + 1);
diff --git a/java/com/google/gerrit/server/schema/Schema_169.java b/java/com/google/gerrit/server/schema/Schema_169.java
new file mode 100644
index 0000000..11aebdd
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/Schema_169.java
@@ -0,0 +1,122 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.notedb.CommentJsonMigrator;
+import com.google.gerrit.server.notedb.MutableNotesMigration;
+import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.update.RefUpdateUtil;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.util.SortedSet;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.PackInserter;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/** Migrate NoteDb inline comments to JSON format. */
+public class Schema_169 extends SchemaVersion {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private final AllUsersName allUsers;
+ private final CommentJsonMigrator migrator;
+ private final GitRepositoryManager repoManager;
+ private final NotesMigration notesMigration;
+
+ @Inject
+ Schema_169(
+ Provider<Schema_168> prior,
+ AllUsersName allUsers,
+ CommentJsonMigrator migrator,
+ GitRepositoryManager repoManager,
+ @GerritServerConfig Config config) {
+ super(prior);
+ this.allUsers = allUsers;
+ this.migrator = migrator;
+ this.repoManager = repoManager;
+ this.notesMigration = MutableNotesMigration.fromConfig(config);
+ }
+
+ @Override
+ protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
+ migrateData(ui);
+ }
+
+ private static ObjectInserter newPackInserter(Repository repo) {
+ if (!(repo instanceof FileRepository)) {
+ return repo.newObjectInserter();
+ }
+ PackInserter ins = ((FileRepository) repo).getObjectDatabase().newPackInserter();
+ ins.checkExisting(false);
+ return ins;
+ }
+
+ @VisibleForTesting
+ protected void migrateData(UpdateUI ui) throws OrmException {
+ // If the migration hasn't started, no need to look for non-JSON
+ if (!notesMigration.commitChangeWrites()) {
+ return;
+ }
+
+ boolean ok = true;
+ ProgressMonitor pm = new TextProgressMonitor();
+ SortedSet<Project.NameKey> projects = repoManager.list();
+ pm.beginTask("Migrating projects", projects.size());
+ int skipped = 0;
+ for (Project.NameKey project : projects) {
+ try (Repository repo = repoManager.openRepository(project);
+ RevWalk rw = new RevWalk(repo);
+ ObjectInserter ins = newPackInserter(repo)) {
+ BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
+ bru.setAllowNonFastForwards(true);
+ ok &= migrator.migrateChanges(project, repo, rw, ins, bru);
+ if (project.equals(allUsers)) {
+ ok &= migrator.migrateDrafts(allUsers, repo, rw, ins, bru);
+ }
+
+ if (!bru.getCommands().isEmpty()) {
+ ins.flush();
+ RefUpdateUtil.executeChecked(bru, rw);
+ } else {
+ skipped++;
+ }
+ } catch (IOException e) {
+ logger.atWarning().log("Error migrating project " + project, e);
+ ok = false;
+ }
+ pm.update(1);
+ }
+ pm.endTask();
+ ui.message(
+ "Skipped " + skipped + " project" + (skipped == 1 ? "" : "s") + " with no legacy comments");
+
+ if (!ok) {
+ throw new OrmException("Migration failed");
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java b/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
index 3c6f6fd..9bf4bb2 100644
--- a/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
+++ b/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
@@ -827,8 +827,12 @@
ReviewDbBatchUpdate.this.logDebug("[" + taskId + "] " + msg, t);
}
- private void logDebug(String msg, Object... args) {
- ReviewDbBatchUpdate.this.logDebug("[" + taskId + "] " + msg, args);
+ private void logDebug(String msg) {
+ ReviewDbBatchUpdate.this.logDebug("[" + taskId + "] " + msg);
+ }
+
+ private void logDebug(String msg, @Nullable Object arg) {
+ ReviewDbBatchUpdate.this.logDebug("[" + taskId + "] " + msg, arg);
}
}
diff --git a/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
similarity index 88%
rename from java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
rename to java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
index 407bbd0..56b00a5 100644
--- a/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
@@ -19,7 +19,7 @@
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.restapi.project.Index;
+import com.google.gerrit.server.restapi.project.IndexChanges;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
@@ -28,10 +28,10 @@
import org.kohsuke.args4j.Argument;
@RequiresAnyCapability({MAINTAIN_SERVER})
-@CommandMetaData(name = "project", description = "Index changes of a project")
-final class IndexProjectCommand extends SshCommand {
+@CommandMetaData(name = "changes-in-project", description = "Index changes of a project")
+final class IndexChangesInProjectCommand extends SshCommand {
- @Inject private Index index;
+ @Inject private IndexChanges index;
@Argument(
index = 0,
diff --git a/java/com/google/gerrit/sshd/commands/IndexCommandsModule.java b/java/com/google/gerrit/sshd/commands/IndexCommandsModule.java
index 599c9dc..332ed69 100644
--- a/java/com/google/gerrit/sshd/commands/IndexCommandsModule.java
+++ b/java/com/google/gerrit/sshd/commands/IndexCommandsModule.java
@@ -40,6 +40,6 @@
command(index, IndexStartCommand.class);
}
command(index, IndexChangesCommand.class);
- command(index, IndexProjectCommand.class);
+ command(index, IndexChangesInProjectCommand.class);
}
}
diff --git a/java/gerrit/AbstractCommitUserIdentityPredicate.java b/java/gerrit/AbstractCommitUserIdentityPredicate.java
index c2aaa76..1bfc95c 100644
--- a/java/gerrit/AbstractCommitUserIdentityPredicate.java
+++ b/java/gerrit/AbstractCommitUserIdentityPredicate.java
@@ -14,9 +14,12 @@
package gerrit;
+import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.UserIdentity;
+import com.google.gerrit.server.account.Emails;
+import com.google.gerrit.server.rules.PrologEnvironment;
import com.googlecode.prolog_cafe.exceptions.PrologException;
+import com.googlecode.prolog_cafe.exceptions.SystemException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.Operation;
import com.googlecode.prolog_cafe.lang.Predicate;
@@ -24,6 +27,8 @@
import com.googlecode.prolog_cafe.lang.StructureTerm;
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+import java.io.IOException;
+import org.eclipse.jgit.lib.PersonIdent;
abstract class AbstractCommitUserIdentityPredicate extends Predicate.P3 {
private static final SymbolTerm user = SymbolTerm.intern("user", 1);
@@ -36,7 +41,7 @@
cont = n;
}
- protected Operation exec(Prolog engine, UserIdentity userId) throws PrologException {
+ protected Operation exec(Prolog engine, PersonIdent userId) throws PrologException {
engine.setB0();
Term a1 = arg1.dereference();
Term a2 = arg2.dereference();
@@ -46,7 +51,18 @@
Term nameTerm = Prolog.Nil;
Term emailTerm = Prolog.Nil;
- Account.Id id = userId.getAccount();
+ PrologEnvironment env = (PrologEnvironment) engine.control;
+ Emails emails = env.getArgs().getEmails();
+ Account.Id id = null;
+ try {
+ ImmutableSet<Account.Id> ids = emails.getAccountForExternal(userId.getEmailAddress());
+ if (ids.size() == 1) {
+ id = ids.iterator().next();
+ }
+ } catch (IOException e) {
+ throw new SystemException(e.getMessage());
+ }
+
if (id == null) {
idTerm = anonymous;
} else {
@@ -58,7 +74,7 @@
nameTerm = SymbolTerm.create(name);
}
- String email = userId.getEmail();
+ String email = userId.getEmailAddress();
if (email != null && !email.equals("")) {
emailTerm = SymbolTerm.create(email);
}
diff --git a/java/gerrit/BUILD b/java/gerrit/BUILD
index 4644af87..8281d8e 100644
--- a/java/gerrit/BUILD
+++ b/java/gerrit/BUILD
@@ -11,5 +11,6 @@
"//lib/flogger:api",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/prolog:runtime",
+ "@guava//jar",
],
)
diff --git a/java/gerrit/PRED_commit_author_3.java b/java/gerrit/PRED_commit_author_3.java
index a876b5e..998b30e 100644
--- a/java/gerrit/PRED_commit_author_3.java
+++ b/java/gerrit/PRED_commit_author_3.java
@@ -14,13 +14,12 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.Operation;
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.Term;
+import org.eclipse.jgit.revwalk.RevCommit;
public class PRED_commit_author_3 extends AbstractCommitUserIdentityPredicate {
public PRED_commit_author_3(Term a1, Term a2, Term a3, Operation n) {
@@ -29,8 +28,7 @@
@Override
public Operation exec(Prolog engine) throws PrologException {
- PatchSetInfo psInfo = StoredValues.PATCH_SET_INFO.get(engine);
- UserIdentity author = psInfo.getAuthor();
- return exec(engine, author);
+ RevCommit revCommit = StoredValues.COMMIT.get(engine);
+ return exec(engine, revCommit.getAuthorIdent());
}
}
diff --git a/java/gerrit/PRED_commit_committer_3.java b/java/gerrit/PRED_commit_committer_3.java
index b24b004..293d8ce 100644
--- a/java/gerrit/PRED_commit_committer_3.java
+++ b/java/gerrit/PRED_commit_committer_3.java
@@ -14,13 +14,12 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.Operation;
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.Term;
+import org.eclipse.jgit.revwalk.RevCommit;
public class PRED_commit_committer_3 extends AbstractCommitUserIdentityPredicate {
public PRED_commit_committer_3(Term a1, Term a2, Term a3, Operation n) {
@@ -29,8 +28,7 @@
@Override
public Operation exec(Prolog engine) throws PrologException {
- PatchSetInfo psInfo = StoredValues.PATCH_SET_INFO.get(engine);
- UserIdentity committer = psInfo.getCommitter();
- return exec(engine, committer);
+ RevCommit revCommit = StoredValues.COMMIT.get(engine);
+ return exec(engine, revCommit.getCommitterIdent());
}
}
diff --git a/java/gerrit/PRED_commit_message_1.java b/java/gerrit/PRED_commit_message_1.java
index 05bb4bb..eb996d6 100644
--- a/java/gerrit/PRED_commit_message_1.java
+++ b/java/gerrit/PRED_commit_message_1.java
@@ -21,6 +21,7 @@
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
+import org.eclipse.jgit.revwalk.RevCommit;
/**
* Returns the commit message as a symbol
@@ -40,7 +41,8 @@
engine.setB0();
Term a1 = arg1.dereference();
- String commitMessage = StoredValues.COMMIT_MESSAGE.get(engine);
+ RevCommit revCommit = StoredValues.COMMIT.get(engine);
+ String commitMessage = revCommit.getFullMessage();
SymbolTerm msg = SymbolTerm.create(commitMessage);
if (!a1.unify(msg, engine.trail)) {
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index ed771af..d64a9d7 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -2713,9 +2713,7 @@
assertThat(commitPatchSetCreation.getShortMessage()).isEqualTo("Create patch set 2");
PersonIdent expectedAuthor =
- changeNoteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(getAccount(admin.id), c.updated, serverIdent.get());
+ changeNoteUtil.newIdent(getAccount(admin.id), c.updated, serverIdent.get());
assertThat(commitPatchSetCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commitPatchSetCreation.getCommitterIdent())
.isEqualTo(new PersonIdent(serverIdent.get(), c.updated));
@@ -2723,10 +2721,7 @@
RevCommit commitChangeCreation = rw.parseCommit(commitPatchSetCreation.getParent(0));
assertThat(commitChangeCreation.getShortMessage()).isEqualTo("Create change");
- expectedAuthor =
- changeNoteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(getAccount(admin.id), c.created, serverIdent.get());
+ expectedAuthor = changeNoteUtil.newIdent(getAccount(admin.id), c.created, serverIdent.get());
assertThat(commitChangeCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commitChangeCreation.getCommitterIdent())
.isEqualTo(new PersonIdent(serverIdent.get(), c.created));
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
index b4a05fc..8479dd1 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -15,10 +15,13 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.AtomicLongMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.NoHttpd;
@@ -39,7 +42,9 @@
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.index.IndexExecutor;
import com.google.inject.Inject;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.PushResult;
@@ -52,6 +57,10 @@
public class ProjectIT extends AbstractDaemonTest {
@Inject private DynamicSet<ProjectIndexedListener> projectIndexedListeners;
+ @Inject
+ @IndexExecutor(BATCH)
+ private ListeningExecutorService executor;
+
private ProjectIndexedCounter projectIndexedCounter;
private RegistrationHandle projectIndexedCounterHandle;
@@ -381,6 +390,26 @@
}
}
+ @Test
+ public void reindexProject() throws Exception {
+ createProject("child", project);
+ projectIndexedCounter.clear();
+
+ gApi.projects().name(allProjects.get()).index(false);
+ projectIndexedCounter.assertReindexOf(allProjects.get());
+ }
+
+ @Test
+ public void reindexProjectWithChildren() throws Exception {
+ Project.NameKey middle = createProject("middle", project);
+ Project.NameKey leave = createProject("leave", middle);
+ projectIndexedCounter.clear();
+
+ gApi.projects().name(project.get()).index(true);
+ projectIndexedCounter.assertReindexExactly(
+ ImmutableMap.of(project.get(), 1L, middle.get(), 1L, leave.get(), 1L));
+ }
+
private ConfigInput createTestConfigInput() {
ConfigInput input = new ConfigInput();
input.description = "some description";
@@ -427,5 +456,10 @@
void assertNoReindex() {
assertThat(countsByProject).isEmpty();
}
+
+ void assertReindexExactly(ImmutableMap<String, Long> expected) {
+ assertThat(countsByProject.asMap()).containsExactlyEntriesIn(expected);
+ clear();
+ }
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index 4c8f53f..51946e9 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -366,6 +366,102 @@
}
@Test
+ public void singleHunkAtBeginningIsFollowedByCorrectCommonLines() throws Exception {
+ String filePath = "a_new_file.txt";
+ String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n";
+ gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent));
+ gApi.changes().id(changeId).edit().publish();
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 1\n", "Line one\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get();
+ assertThat(diffInfo).content().element(0).linesOfA().isNotEmpty();
+ assertThat(diffInfo).content().element(0).linesOfB().isNotEmpty();
+ assertThat(diffInfo)
+ .content()
+ .element(1)
+ .commonLines()
+ .containsExactly("Line 2", "Line 3", "Line 4", "Line 5")
+ .inOrder();
+ }
+
+ @Test
+ public void singleHunkAtEndIsPrecededByCorrectCommonLines() throws Exception {
+ String filePath = "a_new_file.txt";
+ String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n";
+ gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent));
+ gApi.changes().id(changeId).edit().publish();
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 5\n", "Line five\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get();
+ assertThat(diffInfo)
+ .content()
+ .element(0)
+ .commonLines()
+ .containsExactly("Line 1", "Line 2", "Line 3", "Line 4")
+ .inOrder();
+ assertThat(diffInfo).content().element(1).linesOfA().isNotEmpty();
+ assertThat(diffInfo).content().element(1).linesOfB().isNotEmpty();
+ }
+
+ @Test
+ public void singleHunkInTheMiddleIsSurroundedByCorrectCommonLines() throws Exception {
+ String filePath = "a_new_file.txt";
+ String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\n";
+ gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent));
+ gApi.changes().id(changeId).edit().publish();
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 3\n", "Line three\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get();
+ assertThat(diffInfo)
+ .content()
+ .element(0)
+ .commonLines()
+ .containsExactly("Line 1", "Line 2")
+ .inOrder();
+ assertThat(diffInfo).content().element(1).linesOfA().isNotEmpty();
+ assertThat(diffInfo).content().element(1).linesOfB().isNotEmpty();
+ assertThat(diffInfo)
+ .content()
+ .element(2)
+ .commonLines()
+ .containsExactly("Line 4", "Line 5", "Line 6")
+ .inOrder();
+ }
+
+ @Test
+ public void twoHunksAreSeparatedByCorrectCommonLines() throws Exception {
+ String filePath = "a_new_file.txt";
+ String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n";
+ gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent));
+ gApi.changes().id(changeId).edit().publish();
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ addModifiedPatchSet(
+ changeId,
+ filePath,
+ content -> content.replace("Line 2\n", "Line two\n").replace("Line 5\n", "Line five\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get();
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
+ assertThat(diffInfo).content().element(1).linesOfA().isNotEmpty();
+ assertThat(diffInfo).content().element(1).linesOfB().isNotEmpty();
+ assertThat(diffInfo)
+ .content()
+ .element(2)
+ .commonLines()
+ .containsExactly("Line 3", "Line 4")
+ .inOrder();
+ assertThat(diffInfo).content().element(3).linesOfA().isNotEmpty();
+ assertThat(diffInfo).content().element(3).linesOfB().isNotEmpty();
+ }
+
+ @Test
public void rebaseHunksAtStartOfFileAreIdentified() throws Exception {
String newFileContent =
FILE_CONTENT.replace("Line 1\n", "Line one\n").replace("Line 5\n", "Line five\n");
@@ -381,15 +477,15 @@
assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1");
assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one");
assertThat(diffInfo).content().element(0).isDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(3);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 5");
assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line five");
assertThat(diffInfo).content().element(2).isDueToRebase();
- assertThat(diffInfo).content().element(3).commonLines().hasSize(44);
+ assertThat(diffInfo).content().element(3).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 50");
assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line fifty");
assertThat(diffInfo).content().element(4).isNotDueToRebase();
- assertThat(diffInfo).content().element(5).commonLines().hasSize(50);
+ assertThat(diffInfo).content().element(5).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -412,15 +508,15 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(49);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 50");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line fifty");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(9);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 60");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line sixty");
assertThat(diffInfo).content().element(3).isDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(39);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 100");
assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line one hundred");
assertThat(diffInfo).content().element(5).isDueToRebase();
@@ -450,15 +546,15 @@
assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1");
assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one");
assertThat(diffInfo).content().element(0).isNotDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(38);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 40");
assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line forty");
assertThat(diffInfo).content().element(2).isDueToRebase();
- assertThat(diffInfo).content().element(3).commonLines().hasSize(4);
+ assertThat(diffInfo).content().element(3).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 45");
assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line forty five");
assertThat(diffInfo).content().element(4).isDueToRebase();
- assertThat(diffInfo).content().element(5).commonLines().hasSize(54);
+ assertThat(diffInfo).content().element(5).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(6).linesOfA().containsExactly("Line 100");
assertThat(diffInfo).content().element(6).linesOfB().containsExactly("Line one hundred");
assertThat(diffInfo).content().element(6).isNotDueToRebase();
@@ -489,11 +585,11 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(41);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 40");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line forty");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(59);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 100");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line one hundred");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
@@ -522,7 +618,7 @@
assertThat(diffInfo).content().element(0).linesOfA().isNull();
assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line zero");
assertThat(diffInfo).content().element(0).isNotDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(9);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 10");
assertThat(diffInfo)
.content()
@@ -530,11 +626,11 @@
.linesOfB()
.containsExactly("Line ten", "Line ten and a half");
assertThat(diffInfo).content().element(2).isNotDueToRebase();
- assertThat(diffInfo).content().element(3).commonLines().hasSize(29);
+ assertThat(diffInfo).content().element(3).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 40");
assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line forty");
assertThat(diffInfo).content().element(4).isDueToRebase();
- assertThat(diffInfo).content().element(5).commonLines().hasSize(60);
+ assertThat(diffInfo).content().element(5).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -562,11 +658,11 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(37);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 40");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line forty");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(59);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 100");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line one hundred");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
@@ -595,15 +691,15 @@
assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1");
assertThat(diffInfo).content().element(0).linesOfB().isNull();
assertThat(diffInfo).content().element(0).isNotDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(8);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 10", "Line 11");
assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line ten");
assertThat(diffInfo).content().element(2).isNotDueToRebase();
- assertThat(diffInfo).content().element(3).commonLines().hasSize(28);
+ assertThat(diffInfo).content().element(3).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 40");
assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line forty");
assertThat(diffInfo).content().element(4).isDueToRebase();
- assertThat(diffInfo).content().element(5).commonLines().hasSize(60);
+ assertThat(diffInfo).content().element(5).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -623,7 +719,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(39);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 40");
assertThat(diffInfo)
.content()
@@ -631,7 +727,7 @@
.linesOfB()
.containsExactly("Line modified after rebase");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(60);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -655,7 +751,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(38);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 39", "Line 40");
assertThat(diffInfo)
.content()
@@ -663,7 +759,7 @@
.linesOfB()
.containsExactly("Line thirty nine", "Line forty");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(60);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -687,7 +783,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(40);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 41", "Line 42");
assertThat(diffInfo)
.content()
@@ -695,7 +791,7 @@
.linesOfB()
.containsExactly("Line forty one", "Line forty two");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(58);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -717,7 +813,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(38);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo)
.content()
.element(1)
@@ -729,7 +825,7 @@
.linesOfB()
.containsExactly("Line thirty nine", "A different line forty", "Line forty one");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(59);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -755,7 +851,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(40);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 41", "Line 42");
assertThat(diffInfo)
.content()
@@ -763,11 +859,11 @@
.linesOfB()
.containsExactly("Line forty one", "Line forty two", "Line forty two and a half");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(17);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 60");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line sixty");
assertThat(diffInfo).content().element(3).isDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(40);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -791,15 +887,15 @@
assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1");
assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one");
assertThat(diffInfo).content().element(0).isDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 3");
assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line three");
assertThat(diffInfo).content().element(2).isNotDueToRebase();
- assertThat(diffInfo).content().element(3).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(3).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 5");
assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line five");
assertThat(diffInfo).content().element(4).isDueToRebase();
- assertThat(diffInfo).content().element(5).commonLines().hasSize(95);
+ assertThat(diffInfo).content().element(5).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -834,15 +930,15 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 2");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line two");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(7);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 10");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line ten");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(90);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
@@ -879,19 +975,19 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 2");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line two");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(4);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line seven");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 7");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 9");
assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line nine");
assertThat(diffInfo).content().element(5).isNotDueToRebase();
- assertThat(diffInfo).content().element(6).commonLines().hasSize(8);
+ assertThat(diffInfo).content().element(6).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(7).linesOfA().containsExactly("Line 18", "Line 19");
assertThat(diffInfo)
.content()
@@ -899,15 +995,15 @@
.linesOfB()
.containsExactly("Line eighteen", "Line nineteen");
assertThat(diffInfo).content().element(7).isDueToRebase();
- assertThat(diffInfo).content().element(8).commonLines().hasSize(29);
+ assertThat(diffInfo).content().element(8).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(9).linesOfA().containsExactly("Line 50");
assertThat(diffInfo).content().element(9).linesOfB().containsExactly("Line fifty");
assertThat(diffInfo).content().element(9).isDueToRebase();
- assertThat(diffInfo).content().element(10).commonLines().hasSize(9);
+ assertThat(diffInfo).content().element(10).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(11).linesOfA().containsExactly("Line 60");
assertThat(diffInfo).content().element(11).linesOfB().containsExactly("Line sixty");
assertThat(diffInfo).content().element(11).isNotDueToRebase();
- assertThat(diffInfo).content().element(12).commonLines().hasSize(40);
+ assertThat(diffInfo).content().element(12).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
@@ -980,11 +1076,11 @@
assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1");
assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one");
assertThat(diffInfo).content().element(0).isDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(48);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 50");
assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line fifty");
assertThat(diffInfo).content().element(2).isNotDueToRebase();
- assertThat(diffInfo).content().element(3).commonLines().hasSize(50);
+ assertThat(diffInfo).content().element(3).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(initialPatchSetId);
@@ -1015,15 +1111,15 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, renamedFilePath).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(4);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 5");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line five");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(44);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 50");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line fifty");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(50);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
@@ -1059,11 +1155,11 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, newFilePath2).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(4);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 5");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line five");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(95);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
}
@Test
@@ -1096,11 +1192,11 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, newFilePath3).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(4);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 5");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line five");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(95);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
}
@Test
@@ -1125,19 +1221,19 @@
DiffInfo renamedFileDiffInfo =
getDiffRequest(changeId, CURRENT, renamedFileName).withBase(initialPatchSetId).get();
- assertThat(renamedFileDiffInfo).content().element(0).commonLines().hasSize(4);
+ assertThat(renamedFileDiffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(renamedFileDiffInfo).content().element(1).linesOfA().containsExactly("Line 5");
assertThat(renamedFileDiffInfo).content().element(1).linesOfB().containsExactly("Line five");
assertThat(renamedFileDiffInfo).content().element(1).isDueToRebase();
- assertThat(renamedFileDiffInfo).content().element(2).commonLines().hasSize(95);
+ assertThat(renamedFileDiffInfo).content().element(2).commonLines().isNotEmpty();
DiffInfo copiedFileDiffInfo =
getDiffRequest(changeId, CURRENT, copyFileName).withBase(initialPatchSetId).get();
- assertThat(copiedFileDiffInfo).content().element(0).commonLines().hasSize(4);
+ assertThat(copiedFileDiffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(copiedFileDiffInfo).content().element(1).linesOfA().containsExactly("Line 5");
assertThat(copiedFileDiffInfo).content().element(1).linesOfB().containsExactly("Line five");
assertThat(copiedFileDiffInfo).content().element(1).isDueToRebase();
- assertThat(copiedFileDiffInfo).content().element(2).commonLines().hasSize(95);
+ assertThat(copiedFileDiffInfo).content().element(2).commonLines().isNotEmpty();
}
/*
@@ -1168,23 +1264,23 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(4);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line five");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 5");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(14);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 20");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line twenty");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(14);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 35");
assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line thirty five");
assertThat(diffInfo).content().element(5).isDueToRebase();
- assertThat(diffInfo).content().element(6).commonLines().hasSize(24);
+ assertThat(diffInfo).content().element(6).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(7).linesOfA().containsExactly("Line 60");
assertThat(diffInfo).content().element(7).linesOfB().containsExactly("Line sixty");
assertThat(diffInfo).content().element(7).isDueToRebase();
- assertThat(diffInfo).content().element(8).commonLines().hasSize(40);
+ assertThat(diffInfo).content().element(8).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
@@ -1239,19 +1335,19 @@
String currentPatchSetId = gApi.changes().id(changeId).get().currentRevision;
DiffInfo diffInfo =
getDiffRequest(changeId, initialPatchSetId, FILE_NAME).withBase(currentPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(4);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line five");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 5");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(14);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line twenty");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 20");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(14);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(5).linesOfA().isNull();
assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line 35");
assertThat(diffInfo).content().element(5).isDueToRebase();
- assertThat(diffInfo).content().element(6).commonLines().hasSize(65);
+ assertThat(diffInfo).content().element(6).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).revision(initialPatchSetId).files(currentPatchSetId);
@@ -1282,7 +1378,7 @@
.intralineEditsOfB()
.containsExactly(ImmutableList.of(5, 3));
assertThat(diffInfo).content().element(0).isNotDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(99);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
}
@Test
@@ -1312,11 +1408,11 @@
.intralineEditsOfB()
.containsExactly(ImmutableList.of(5, 3));
assertThat(diffInfo).content().element(0).isDueToRebase();
- assertThat(diffInfo).content().element(1).commonLines().hasSize(48);
+ assertThat(diffInfo).content().element(1).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 50");
assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line fifty");
assertThat(diffInfo).content().element(2).isNotDueToRebase();
- assertThat(diffInfo).content().element(3).commonLines().hasSize(50);
+ assertThat(diffInfo).content().element(3).commonLines().isNotEmpty();
}
@Test
@@ -1335,7 +1431,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(3);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4", "{", "Line 6");
assertThat(diffInfo)
.content()
@@ -1343,7 +1439,7 @@
.linesOfB()
.containsExactly("Line four", "{", "Line six");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(94);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
@@ -1372,19 +1468,19 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(3);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line four");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 6");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line six");
assertThat(diffInfo).content().element(3).isDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(13);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 20");
assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line twenty");
assertThat(diffInfo).content().element(5).isNotDueToRebase();
- assertThat(diffInfo).content().element(6).commonLines().hasSize(80);
+ assertThat(diffInfo).content().element(6).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
@@ -1411,19 +1507,19 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(3);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4");
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line four");
assertThat(diffInfo).content().element(1).isDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 6");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line six");
assertThat(diffInfo).content().element(3).isNotDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 8");
assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line eight");
assertThat(diffInfo).content().element(5).isDueToRebase();
- assertThat(diffInfo).content().element(6).commonLines().hasSize(92);
+ assertThat(diffInfo).content().element(6).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
@@ -1452,7 +1548,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
- assertThat(diffInfo).content().element(0).commonLines().hasSize(3);
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4", "{", "Line 6");
assertThat(diffInfo)
.content()
@@ -1460,11 +1556,11 @@
.linesOfB()
.containsExactly("Line four", "{", "Line six");
assertThat(diffInfo).content().element(1).isNotDueToRebase();
- assertThat(diffInfo).content().element(2).commonLines().hasSize(1);
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 8");
assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line eight!");
assertThat(diffInfo).content().element(3).isDueToRebase();
- assertThat(diffInfo).content().element(4).commonLines().hasSize(92);
+ assertThat(diffInfo).content().element(4).commonLines().isNotEmpty();
Map<String, FileInfo> changedFiles =
gApi.changes().id(changeId).current().files(previousPatchSetId);
diff --git a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
index b362a36..80430c4 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
@@ -336,8 +336,7 @@
c ->
cfg.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true)
.getPermission(c, true)
- .getRules()
- .clear());
+ .clearRules());
}
private PushResult push(String... refSpecs) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 1de9d29..45294fb 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -433,9 +433,7 @@
if (notesMigration.commitChangeWrites()) {
PersonIdent committer = serverIdent.get();
PersonIdent author =
- noteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
+ noteUtil.newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
tr.branch(RefNames.changeMetaRef(c3.getId()))
.commit()
.author(author)
diff --git a/javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java b/javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java
index 26bc345..2bb3dca 100644
--- a/javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java
+++ b/javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.apache.http.HttpStatus.SC_FORBIDDEN;
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
+import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
import com.google.auto.value.AutoValue;
@@ -80,8 +81,13 @@
String msg = String.format("%s %s returned %d: %s", method, uri, status, body);
if (restCall.expectedResponseCode().isPresent()) {
assertWithMessage(msg).that(status).isEqualTo(restCall.expectedResponseCode().get());
+ if (restCall.expectedMessage().isPresent()) {
+ assertWithMessage(msg).that(body).contains(restCall.expectedMessage().get());
+ }
} else {
- assertWithMessage(msg).that(status).isNotIn(ImmutableList.of(SC_FORBIDDEN, SC_NOT_FOUND));
+ assertWithMessage(msg)
+ .that(status)
+ .isNotIn(ImmutableList.of(SC_FORBIDDEN, SC_NOT_FOUND, SC_METHOD_NOT_ALLOWED));
assertWithMessage(msg).that(status).isLessThan(SC_INTERNAL_SERVER_ERROR);
}
}
@@ -123,6 +129,8 @@
abstract Optional<Integer> expectedResponseCode();
+ abstract Optional<String> expectedMessage();
+
String uri(String... args) {
String uriFormat = uriFormat();
int expectedArgNum = StringUtils.countMatches(uriFormat, "%s");
@@ -144,6 +152,8 @@
abstract Builder expectedResponseCode(int expectedResponseCode);
+ abstract Builder expectedMessage(String expectedMessage);
+
abstract RestCall build();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java
index 8de85a9..1a401b0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java
@@ -14,9 +14,20 @@
package com.google.gerrit.acceptance.rest;
+import static com.google.gerrit.acceptance.rest.AbstractRestApiBindingsTest.Method.PUT;
+import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithoutExpiration;
+import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
+
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.extensions.common.ChangeInput;
+import com.google.gerrit.gpg.testing.TestKey;
+import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
import org.junit.Test;
/**
@@ -27,6 +38,8 @@
* AbstractRestApiBindingsTest}).
*/
public class AccountsRestApiBindingsIT extends AbstractRestApiBindingsTest {
+ @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
+
/**
* Account REST endpoints to be tested, each URL contains a placeholder for the account
* identifier.
@@ -40,7 +53,11 @@
RestCall.put("/accounts/%s/name"),
RestCall.delete("/accounts/%s/name"),
RestCall.get("/accounts/%s/username"),
- RestCall.put("/accounts/%s/username"),
+ RestCall.builder(PUT, "/accounts/%s/username")
+ // Changing the username is not allowed.
+ .expectedResponseCode(SC_METHOD_NOT_ALLOWED)
+ .expectedMessage("Username cannot be changed.")
+ .build(),
RestCall.get("/accounts/%s/active"),
RestCall.put("/accounts/%s/active"),
RestCall.delete("/accounts/%s/active"),
@@ -73,7 +90,9 @@
RestCall.post("/accounts/%s/external.ids:delete"),
RestCall.get("/accounts/%s/oauthtoken"),
RestCall.get("/accounts/%s/capabilities"),
- RestCall.get("/accounts/%s/capabilities/viewPlugins"));
+ RestCall.get("/accounts/%s/capabilities/viewPlugins"),
+ RestCall.get("/accounts/%s/gpgkeys"),
+ RestCall.post("/accounts/%s/gpgkeys"));
/**
* Email REST endpoints to be tested, each URL contains a placeholders for the account and email
@@ -89,6 +108,17 @@
RestCall.delete("/accounts/%s/emails/%s"));
/**
+ * GPG key REST endpoints to be tested, each URL contains a placeholders for the account
+ * identifier and the GPG key identifier.
+ */
+ private static final ImmutableList<RestCall> GPG_KEY_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/accounts/%s/gpgkeys/%s"),
+
+ // GPG key deletion must be tested last
+ RestCall.delete("/accounts/%s/gpgkeys/%s"));
+
+ /**
* SSH key REST endpoints to be tested, each URL contains a placeholders for the account and SSH
* key identifier.
*/
@@ -121,6 +151,30 @@
}
@Test
+ @GerritConfig(name = "receive.enableSignedPush", value = "true")
+ public void gpgKeyEndpoints() throws Exception {
+ TestKey key = validKeyWithoutExpiration();
+ String id = key.getKeyIdString();
+
+ String email = "test1@example.com"; // email that is hard-coded in the test GPG key
+ accountsUpdateProvider
+ .get()
+ .update(
+ "Add Email",
+ admin.getId(),
+ u ->
+ u.addExternalId(
+ ExternalId.createWithEmail(name("test"), email, admin.getId(), email)));
+
+ setApiUser(admin);
+ gApi.accounts()
+ .self()
+ .putGpgKeys(ImmutableList.of(key.getPublicKeyArmored()), ImmutableList.of());
+
+ execute(GPG_KEY_ENDPOINTS, "self", id);
+ }
+
+ @Test
@UseSsh
public void sshKeyEndpoints() throws Exception {
String sshKeySeq = Integer.toString(gApi.accounts().self().listSshKeys().size());
diff --git a/javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java
index 53c544d..59c0903 100644
--- a/javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java
@@ -59,7 +59,6 @@
RestCall.delete("/changes/%s/topic"),
RestCall.get("/changes/%s/in"),
RestCall.get("/changes/%s/hashtags"),
- RestCall.post("/changes/%s/hashtags"),
RestCall.get("/changes/%s/comments"),
RestCall.get("/changes/%s/robotcomments"),
RestCall.get("/changes/%s/drafts"),
@@ -98,6 +97,7 @@
.expectedResponseCode(SC_NOT_FOUND)
.build(),
RestCall.get("/changes/%s/edit"),
+ RestCall.post("/changes/%s/edit"),
RestCall.post("/changes/%s/edit:rebase"),
RestCall.get("/changes/%s/edit:message"),
RestCall.put("/changes/%s/edit:message"),
@@ -115,7 +115,8 @@
* identifier.
*/
private static final ImmutableList<RestCall> CHANGE_ENDPOINTS_NOTEDB =
- ImmutableList.of(RestCall.post("/changes/%s/rebuild.notedb"));
+ ImmutableList.of(
+ RestCall.post("/changes/%s/hashtags"), RestCall.post("/changes/%s/rebuild.notedb"));
/**
* Reviewer REST endpoints to be tested, each URL contains placeholders for the change identifier
@@ -239,7 +240,38 @@
RestCall.get("/changes/%s/revisions/%s/files/%s/diff"),
RestCall.get("/changes/%s/revisions/%s/files/%s/blame"));
- // TODO(ekempin): Add tests for change message and change edit REST endpoints
+ /**
+ * Change message REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier and the change message identifier.
+ */
+ private static final ImmutableList<RestCall> CHANGE_MESSAGE_ENDPOINTS =
+ ImmutableList.of(RestCall.get("/changes/%s/messages/%s"));
+
+ /**
+ * Change edit REST endpoints that create an edit to be tested, each URL contains placeholders for
+ * the change identifier and the change edit identifier.
+ */
+ private static final ImmutableList<RestCall> CHANGE_EDIT_CREATE_ENDPOINTS =
+ ImmutableList.of(
+ // Create change edit by editing an existing file.
+ RestCall.put("/changes/%s/edit/%s"),
+
+ // Create change edit by deleting an existing file.
+ RestCall.delete("/changes/%s/edit/%s"));
+
+ /**
+ * Change edit REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier and the change edit identifier.
+ */
+ private static final ImmutableList<RestCall> CHANGE_EDIT_ENDPOINTS =
+ ImmutableList.of(
+ // Calls on existing change edit.
+ RestCall.get("/changes/%s/edit/%s"),
+ RestCall.put("/changes/%s/edit/%s"),
+ RestCall.get("/changes/%s/edit/%s/meta"),
+
+ // Delete content of a file in an existing change edit.
+ RestCall.delete("/changes/%s/edit/%s"));
private static final String FILENAME = "test.txt";
@@ -426,6 +458,35 @@
execute(REVISION_FILE_ENDPOINTS, changeId, "current", FILENAME);
}
+ @Test
+ public void changeMessageEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ // A change message is created on change creation.
+ String changeMessageId = Iterables.getOnlyElement(gApi.changes().id(changeId).messages()).id;
+
+ execute(CHANGE_MESSAGE_ENDPOINTS, changeId, changeMessageId);
+ }
+
+ @Test
+ public void changeEditCreateEndpoints() throws Exception {
+ String changeId = createChange("Subject", FILENAME, "content").getChangeId();
+
+ // Each of the REST calls creates the change edit newly.
+ execute(
+ CHANGE_EDIT_CREATE_ENDPOINTS,
+ () -> adminRestSession.delete("/changes/" + changeId + "/edit"),
+ changeId,
+ FILENAME);
+ }
+
+ @Test
+ public void changeEditEndpoints() throws Exception {
+ String changeId = createChange("Subject", FILENAME, "content").getChangeId();
+ gApi.changes().id(changeId).edit().create();
+ execute(CHANGE_EDIT_ENDPOINTS, changeId, FILENAME);
+ }
+
private static Comment.Range createRange(
int startLine, int startCharacter, int endLine, int endCharacter) {
Comment.Range range = new Comment.Range();
diff --git a/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
index b930b20..508d407 100644
--- a/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
@@ -56,6 +56,7 @@
RestCall.get("/config/server/summary"),
RestCall.get("/config/server/capabilities"),
RestCall.get("/config/server/caches"),
+ RestCall.post("/config/server/caches"),
RestCall.get("/config/server/tasks"));
/**
diff --git a/javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java
index aba677f..07ea3d0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java
@@ -42,9 +42,9 @@
RestCall.get("/plugins/%s/gerrit~status"),
// POST (and PUT) requests don't require the 'gerrit~' prefix in front of the view name.
- RestCall.post("/plugins/%s/enable"),
- RestCall.post("/plugins/%s/disable"),
- RestCall.post("/plugins/%s/reload"),
+ RestCall.post("/plugins/%s/gerrit~enable"),
+ RestCall.post("/plugins/%s/gerrit~disable"),
+ RestCall.post("/plugins/%s/gerrit~reload"),
// Plugin deletion must be tested last
RestCall.delete("/plugins/%s"));
diff --git a/javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java
index d59f037..a86a739 100644
--- a/javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java
@@ -19,6 +19,7 @@
import static com.google.gerrit.acceptance.rest.AbstractRestApiBindingsTest.Method.GET;
import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
import static com.google.gerrit.server.restapi.project.DashboardsCollection.DEFAULT_DASHBOARD_NAME;
+import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
import com.google.common.collect.ImmutableList;
@@ -93,7 +94,11 @@
RestCall.get("/projects/%s/branches/%s"),
RestCall.put("/projects/%s/branches/%s"),
RestCall.get("/projects/%s/branches/%s/mergeable"),
- RestCall.get("/projects/%s/branches/%s/reflog"),
+ RestCall.builder(GET, "/projects/%s/branches/%s/reflog")
+ // The tests use DfsRepository which does not support getting the reflog.
+ .expectedResponseCode(SC_METHOD_NOT_ALLOWED)
+ .expectedMessage("reflog not supported on")
+ .build(),
RestCall.builder(GET, "/projects/%s/branches/%s/files")
// GET /projects/<project>/branches/<branch>/files is not implemented
.expectedResponseCode(SC_NOT_FOUND)
diff --git a/javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java
index 131ed1a..a2c4ea6 100644
--- a/javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java
@@ -18,6 +18,7 @@
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.GerritConfig;
import org.junit.Test;
/**
@@ -39,6 +40,7 @@
.expectedResponseCode(SC_NOT_FOUND)
.build(),
RestCall.get("/changes/"),
+ RestCall.post("/changes/"),
RestCall.get("/groups/"),
RestCall.put("/groups/new-group"),
RestCall.get("/plugins/"),
@@ -47,6 +49,7 @@
RestCall.put("/projects/new-project"));
@Test
+ @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
public void rootEndpoints() throws Exception {
execute(ROOT_ENDPOINTS);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
index e6f61fa..eb80092 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
@@ -189,7 +189,6 @@
testVoteOnBehalfOfWithComment();
}
- @GerritConfig(name = "notedb.writeJson", value = "true")
@Test
public void voteOnBehalfOfWithCommentWritingJson() throws Exception {
assume().that(notesMigration.readChanges()).isTrue();
@@ -225,7 +224,6 @@
assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id);
}
- @GerritConfig(name = "notedb.writeJson", value = "true")
@Test
public void voteOnBehalfOfWithRobotComment() throws Exception {
assume().that(notesMigration.readChanges()).isTrue();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index c723082..09c7e0b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -289,9 +289,7 @@
assertThat(commit.getShortMessage()).isEqualTo("Create change");
PersonIdent expectedAuthor =
- changeNoteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(getAccount(admin.id), c.created, serverIdent.get());
+ changeNoteUtil.newIdent(getAccount(admin.id), c.created, serverIdent.get());
assertThat(commit.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commit.getCommitterIdent())
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
index 5555185..211af59 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -950,8 +950,6 @@
@Test
public void jsonCommentHasLegacyFormatFalse() throws Exception {
assume().that(notesMigration.readChanges()).isTrue();
- assertThat(noteUtil.getChangeNoteJson().getWriteJson()).isTrue();
-
PushOneCommit.Result result = createChange();
Change.Id changeId = result.getChange().getId();
addComment(result.getChangeId(), "comment");
diff --git a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index ea44bd7..29c043a 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -902,9 +902,7 @@
}
PersonIdent committer = serverIdent.get();
PersonIdent author =
- noteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
+ noteUtil.newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
serverSideTestRepo
.branch(RefNames.changeMetaRef(id))
.commit()
diff --git a/javatests/com/google/gerrit/acceptance/server/change/LegacyCommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/LegacyCommentsIT.java
deleted file mode 100644
index e029e7a..0000000
--- a/javatests/com/google/gerrit/acceptance/server/change/LegacyCommentsIT.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.server.change;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.server.notedb.ChangeNoteUtil;
-import com.google.gerrit.testing.ConfigSuite;
-import com.google.inject.Inject;
-import java.util.Collection;
-import org.eclipse.jgit.lib.Config;
-import org.junit.Before;
-import org.junit.Test;
-
-@NoHttpd
-public class LegacyCommentsIT extends AbstractDaemonTest {
- @Inject private ChangeNoteUtil noteUtil;
-
- @ConfigSuite.Default
- public static Config writeJsonFalseConfig() {
- Config c = new Config();
- c.setBoolean("noteDb", null, "writeJson", false);
- return c;
- }
-
- @Before
- public void setUp() {
- setApiUser(user);
- }
-
- @Test
- public void legacyCommentHasLegacyFormatTrue() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
- assertThat(noteUtil.getChangeNoteJson().getWriteJson()).isFalse();
-
- PushOneCommit.Result result = createChange();
- Change.Id changeId = result.getChange().getId();
-
- CommentInput cin = new CommentInput();
- cin.message = "comment";
- cin.path = PushOneCommit.FILE_NAME;
-
- ReviewInput rin = new ReviewInput();
- rin.comments = ImmutableMap.of(cin.path, ImmutableList.of(cin));
- gApi.changes().id(changeId.get()).current().review(rin);
-
- Collection<Comment> comments =
- notesFactory.createChecked(db, project, changeId).getComments().values();
- assertThat(comments).hasSize(1);
- Comment comment = comments.iterator().next();
- assertThat(comment.message).isEqualTo("comment");
- assertThat(comment.legacyFormat).isTrue();
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java
index 834dbfa..bcae987 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java
@@ -25,16 +25,10 @@
import java.io.File;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.Repository;
-import org.junit.Before;
import org.junit.Test;
@UseLocalDisk
public class ReflogIT extends AbstractDaemonTest {
- @Before
- public void setUp() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
- }
-
@Test
public void guessRestApiInReflog() throws Exception {
assume().that(notesMigration.disableChangeReviewDb()).isTrue();
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
index bf01a21..98dca3e 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
@@ -71,21 +71,17 @@
@Test
public void testUserPredicate() throws Exception {
- // This test results in a RULE_ERROR as Prolog tries to find accounts by email, using the index.
- // TODO(maximeg) get OK results
modifySubmitRules(
String.format(
"gerrit:commit_author(user(%d), '%s', '%s')",
user.getId().get(), user.fullName, user.email));
- assertThat(statusForRule()).isEqualTo(SubmitRecord.Status.RULE_ERROR);
+ assertThat(statusForRule()).isEqualTo(SubmitRecord.Status.OK);
}
@Test
public void testCommitAuthorPredicate() throws Exception {
- // This test results in a RULE_ERROR as Prolog tries to find accounts by email, using the index.
- // TODO(maximeg) get OK results
modifySubmitRules("gerrit:commit_author(Id)");
- assertThat(statusForRule()).isEqualTo(SubmitRecord.Status.RULE_ERROR);
+ assertThat(statusForRule()).isEqualTo(SubmitRecord.Status.OK);
}
private SubmitRecord.Status statusForRule() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
index 208f380..25bb7a6 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
@@ -102,7 +102,7 @@
enableChangeIndexWrites();
changeIndexedCounter.clear();
- String cmd = Joiner.on(" ").join("gerrit", "index", "project", project.get());
+ String cmd = Joiner.on(" ").join("gerrit", "index", "changes-in-project", project.get());
adminSshSession.exec(cmd);
adminSshSession.assertSuccess();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
index 5694bd0..cc86c0b 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
@@ -93,7 +93,8 @@
}
}),
"index",
- ImmutableList.of("changes", "project"), // "activate" and "start" are not included
+ ImmutableList.of(
+ "changes", "changes-in-project"), // "activate" and "start" are not included
"logging",
ImmutableList.of("ls", "set"),
"plugin",
diff --git a/javatests/com/google/gerrit/common/data/AccessSectionTest.java b/javatests/com/google/gerrit/common/data/AccessSectionTest.java
new file mode 100644
index 0000000..ecdb3c8
--- /dev/null
+++ b/javatests/com/google/gerrit/common/data/AccessSectionTest.java
@@ -0,0 +1,253 @@
+// 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.common.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class AccessSectionTest {
+ @Rule public ExpectedException exception = ExpectedException.none();
+
+ private static final String REF_PATTERN = "refs/heads/master";
+
+ private AccessSection accessSection;
+
+ @Before
+ public void setup() {
+ this.accessSection = new AccessSection(REF_PATTERN);
+ }
+
+ @Test
+ public void getName() {
+ assertThat(accessSection.getName()).isEqualTo(REF_PATTERN);
+ }
+
+ @Test
+ public void getEmptyPermissions() {
+ assertThat(accessSection.getPermissions()).isNotNull();
+ assertThat(accessSection.getPermissions()).isEmpty();
+ }
+
+ @Test
+ public void setAndGetPermissions() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+ accessSection.setPermissions(ImmutableList.of(abandonPermission, rebasePermission));
+ assertThat(accessSection.getPermissions())
+ .containsExactly(abandonPermission, rebasePermission)
+ .inOrder();
+
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+ accessSection.setPermissions(ImmutableList.of(submitPermission));
+ assertThat(accessSection.getPermissions()).containsExactly(submitPermission);
+ }
+
+ @Test
+ public void cannotSetDuplicatePermissions() {
+ exception.expect(IllegalArgumentException.class);
+ accessSection.setPermissions(
+ ImmutableList.of(new Permission(Permission.ABANDON), new Permission(Permission.ABANDON)));
+ }
+
+ @Test
+ public void cannotSetPermissionsWithConflictingNames() {
+ Permission abandonPermissionLowerCase =
+ new Permission(Permission.ABANDON.toLowerCase(Locale.US));
+ Permission abandonPermissionUpperCase =
+ new Permission(Permission.ABANDON.toUpperCase(Locale.US));
+
+ exception.expect(IllegalArgumentException.class);
+ accessSection.setPermissions(
+ ImmutableList.of(abandonPermissionLowerCase, abandonPermissionUpperCase));
+ }
+
+ @Test
+ public void getNonExistingPermission() {
+ assertThat(accessSection.getPermission("non-existing")).isNull();
+ assertThat(accessSection.getPermission("non-existing", false)).isNull();
+ }
+
+ @Test
+ public void getPermission() {
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+ accessSection.setPermissions(ImmutableList.of(submitPermission));
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isEqualTo(submitPermission);
+ }
+
+ @Test
+ public void getPermissionWithOtherCase() {
+ Permission submitPermissionLowerCase = new Permission(Permission.SUBMIT.toLowerCase(Locale.US));
+ accessSection.setPermissions(ImmutableList.of(submitPermissionLowerCase));
+ assertThat(accessSection.getPermission(Permission.SUBMIT.toUpperCase(Locale.US)))
+ .isEqualTo(submitPermissionLowerCase);
+ }
+
+ @Test
+ public void createMissingPermissionOnGet() {
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+
+ assertThat(accessSection.getPermission(Permission.SUBMIT, true))
+ .isEqualTo(new Permission(Permission.SUBMIT));
+ }
+
+ @Test
+ public void addPermission() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+
+ accessSection.setPermissions(ImmutableList.of(abandonPermission, rebasePermission));
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+ accessSection.addPermission(submitPermission);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isEqualTo(submitPermission);
+ assertThat(accessSection.getPermissions())
+ .containsExactly(abandonPermission, rebasePermission, submitPermission)
+ .inOrder();
+ }
+
+ @Test
+ public void cannotAddPermissionByModifyingListThatWasProvidedToAccessSection() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+
+ List<Permission> permissions = new ArrayList<>();
+ permissions.add(abandonPermission);
+ permissions.add(rebasePermission);
+ accessSection.setPermissions(permissions);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+ permissions.add(submitPermission);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+ }
+
+ @Test
+ public void cannotAddPermissionByModifyingListThatWasRetrievedFromAccessSection() {
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+ accessSection.getPermissions().add(submitPermission);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+
+ List<Permission> permissions = new ArrayList<>();
+ permissions.add(new Permission(Permission.ABANDON));
+ permissions.add(new Permission(Permission.REBASE));
+ accessSection.setPermissions(permissions);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+ accessSection.getPermissions().add(submitPermission);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+ }
+
+ @Test
+ public void removePermission() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+
+ accessSection.setPermissions(
+ ImmutableList.of(abandonPermission, rebasePermission, submitPermission));
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNotNull();
+
+ accessSection.remove(submitPermission);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+ assertThat(accessSection.getPermissions())
+ .containsExactly(abandonPermission, rebasePermission)
+ .inOrder();
+ }
+
+ @Test
+ public void removePermissionByName() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+
+ accessSection.setPermissions(
+ ImmutableList.of(abandonPermission, rebasePermission, submitPermission));
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNotNull();
+
+ accessSection.removePermission(Permission.SUBMIT);
+ assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
+ assertThat(accessSection.getPermissions())
+ .containsExactly(abandonPermission, rebasePermission)
+ .inOrder();
+ }
+
+ @Test
+ public void removePermissionByNameOtherCase() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+
+ String submitLowerCase = Permission.SUBMIT.toLowerCase(Locale.US);
+ String submitUpperCase = Permission.SUBMIT.toUpperCase(Locale.US);
+ Permission submitPermissionLowerCase = new Permission(submitLowerCase);
+
+ accessSection.setPermissions(
+ ImmutableList.of(abandonPermission, rebasePermission, submitPermissionLowerCase));
+ assertThat(accessSection.getPermission(submitLowerCase)).isNotNull();
+ assertThat(accessSection.getPermission(submitUpperCase)).isNotNull();
+
+ accessSection.removePermission(submitUpperCase);
+ assertThat(accessSection.getPermission(submitLowerCase)).isNull();
+ assertThat(accessSection.getPermission(submitUpperCase)).isNull();
+ assertThat(accessSection.getPermissions())
+ .containsExactly(abandonPermission, rebasePermission)
+ .inOrder();
+ }
+
+ @Test
+ public void mergeAccessSections() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+ Permission submitPermission = new Permission(Permission.SUBMIT);
+
+ AccessSection accessSection1 = new AccessSection("refs/heads/foo");
+ accessSection1.setPermissions(ImmutableList.of(abandonPermission, rebasePermission));
+
+ AccessSection accessSection2 = new AccessSection("refs/heads/bar");
+ accessSection2.setPermissions(ImmutableList.of(rebasePermission, submitPermission));
+
+ accessSection1.mergeFrom(accessSection2);
+ assertThat(accessSection1.getPermissions())
+ .containsExactly(abandonPermission, rebasePermission, submitPermission)
+ .inOrder();
+ }
+
+ @Test
+ public void testEquals() {
+ Permission abandonPermission = new Permission(Permission.ABANDON);
+ Permission rebasePermission = new Permission(Permission.REBASE);
+
+ accessSection.setPermissions(ImmutableList.of(abandonPermission, rebasePermission));
+
+ AccessSection accessSectionSamePermissionsOtherRef = new AccessSection("refs/heads/other");
+ accessSectionSamePermissionsOtherRef.setPermissions(
+ ImmutableList.of(abandonPermission, rebasePermission));
+ assertThat(accessSection.equals(accessSectionSamePermissionsOtherRef)).isFalse();
+
+ AccessSection accessSectionOther = new AccessSection(REF_PATTERN);
+ accessSectionOther.setPermissions(ImmutableList.of(abandonPermission));
+ assertThat(accessSection.equals(accessSectionOther)).isFalse();
+
+ accessSectionOther.addPermission(rebasePermission);
+ assertThat(accessSection.equals(accessSectionOther)).isTrue();
+ }
+}
diff --git a/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java b/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
index 4c4c769..dcd3c05 100644
--- a/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
+++ b/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
@@ -14,20 +14,20 @@
package com.google.gerrit.common.data;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
public class EncodePathSeparatorTest {
@Test
public void defaultBehaviour() {
- assertEquals("a/b", new GitwebType().replacePathSeparator("a/b"));
+ assertThat(new GitwebType().replacePathSeparator("a/b")).isEqualTo("a/b");
}
@Test
public void exclamationMark() {
GitwebType gitwebType = new GitwebType();
gitwebType.setPathSeparator('!');
- assertEquals("a!b", gitwebType.replacePathSeparator("a/b"));
+ assertThat(gitwebType.replacePathSeparator("a/b")).isEqualTo("a!b");
}
}
diff --git a/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java b/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
index 0f067c4..b646d2b 100644
--- a/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
+++ b/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
@@ -14,9 +14,7 @@
package com.google.gerrit.common.data;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
@@ -26,155 +24,148 @@
public class ParameterizedStringTest {
@Test
public void emptyString() {
- final ParameterizedString p = new ParameterizedString("");
- assertEquals("", p.getPattern());
- assertEquals("", p.getRawPattern());
- assertTrue(p.getParameterNames().isEmpty());
+ ParameterizedString p = new ParameterizedString("");
+ assertThat(p.getPattern()).isEmpty();
+ assertThat(p.getRawPattern()).isEmpty();
+ assertThat(p.getParameterNames()).isEmpty();
- final Map<String, String> a = new HashMap<>();
- assertNotNull(p.bind(a));
- assertEquals(0, p.bind(a).length);
- assertEquals("", p.replace(a));
+ Map<String, String> a = new HashMap<>();
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).isEmpty();
+ assertThat(p.replace(a)).isEmpty();
}
@Test
public void asis1() {
- final ParameterizedString p = ParameterizedString.asis("${bar}c");
- assertEquals("${bar}c", p.getPattern());
- assertEquals("${bar}c", p.getRawPattern());
- assertTrue(p.getParameterNames().isEmpty());
+ ParameterizedString p = ParameterizedString.asis("${bar}c");
+ assertThat(p.getPattern()).isEqualTo("${bar}c");
+ assertThat(p.getRawPattern()).isEqualTo("${bar}c");
+ assertThat(p.getParameterNames()).isEmpty();
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("bar", "frobinator");
- assertNotNull(p.bind(a));
- assertEquals(0, p.bind(a).length);
- assertEquals("${bar}c", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).isEmpty();
+ assertThat(p.replace(a)).isEqualTo("${bar}c");
}
@Test
public void replace1() {
- final ParameterizedString p = new ParameterizedString("${bar}c");
- assertEquals("${bar}c", p.getPattern());
- assertEquals("{0}c", p.getRawPattern());
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("bar"));
+ ParameterizedString p = new ParameterizedString("${bar}c");
+ assertThat(p.getPattern()).isEqualTo("${bar}c");
+ assertThat(p.getRawPattern()).isEqualTo("{0}c");
+ assertThat(p.getParameterNames()).containsExactly("bar");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("bar", "frobinator");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("frobinator", p.bind(a)[0]);
- assertEquals("frobinatorc", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("frobinator");
+ assertThat(p.replace(a)).isEqualTo("frobinatorc");
}
@Test
public void replace2() {
- final ParameterizedString p = new ParameterizedString("a${bar}c");
- assertEquals("a${bar}c", p.getPattern());
- assertEquals("a{0}c", p.getRawPattern());
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("bar"));
+ ParameterizedString p = new ParameterizedString("a${bar}c");
+ assertThat(p.getPattern()).isEqualTo("a${bar}c");
+ assertThat(p.getRawPattern()).isEqualTo("a{0}c");
+ assertThat(p.getParameterNames()).containsExactly("bar");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("bar", "frobinator");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("frobinator", p.bind(a)[0]);
- assertEquals("afrobinatorc", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("frobinator");
+ assertThat(p.replace(a)).isEqualTo("afrobinatorc");
}
@Test
public void replace3() {
- final ParameterizedString p = new ParameterizedString("a${bar}");
- assertEquals("a${bar}", p.getPattern());
- assertEquals("a{0}", p.getRawPattern());
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("bar"));
+ ParameterizedString p = new ParameterizedString("a${bar}");
+ assertThat(p.getPattern()).isEqualTo("a${bar}");
+ assertThat(p.getRawPattern()).isEqualTo("a{0}");
+ assertThat(p.getParameterNames()).containsExactly("bar");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("bar", "frobinator");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("frobinator", p.bind(a)[0]);
- assertEquals("afrobinator", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("frobinator");
+ assertThat(p.replace(a)).isEqualTo("afrobinator");
}
@Test
public void replace4() {
- final ParameterizedString p = new ParameterizedString("a${bar}c");
- assertEquals("a${bar}c", p.getPattern());
- assertEquals("a{0}c", p.getRawPattern());
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("bar"));
+ ParameterizedString p = new ParameterizedString("a${bar}c");
+ assertThat(p.getPattern()).isEqualTo("a${bar}c");
+ assertThat(p.getRawPattern()).isEqualTo("a{0}c");
+ assertThat(p.getParameterNames()).containsExactly("bar");
- final Map<String, String> a = new HashMap<>();
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("", p.bind(a)[0]);
- assertEquals("ac", p.replace(a));
+ Map<String, String> a = new HashMap<>();
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEmpty();
+ assertThat(p.replace(a)).isEqualTo("ac");
}
@Test
public void replaceToLowerCase() {
- final ParameterizedString p = new ParameterizedString("${a.toLowerCase}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toLowerCase}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "foo");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
a.put("a", "FOO");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
}
@Test
public void replaceToUpperCase() {
- final ParameterizedString p = new ParameterizedString("${a.toUpperCase}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toUpperCase}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "foo");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO", p.bind(a)[0]);
- assertEquals("FOO", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO");
+ assertThat(p.replace(a)).isEqualTo("FOO");
a.put("a", "FOO");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO", p.bind(a)[0]);
- assertEquals("FOO", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO");
+ assertThat(p.replace(a)).isEqualTo("FOO");
}
@Test
public void replaceLocalName() {
- final ParameterizedString p = new ParameterizedString("${a.localPart}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.localPart}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
a.put("a", "foo");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
}
@Test
@@ -182,226 +173,216 @@
ParameterizedString p =
new ParameterizedString(
"hi, ${userName.toUpperCase},your eamil address is '${email.toLowerCase.localPart}'.right?");
- assertEquals(2, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("userName"));
- assertTrue(p.getParameterNames().contains("email"));
+ assertThat(p.getParameterNames()).containsExactly("userName", "email");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("userName", "firstName lastName");
a.put("email", "FIRSTNAME.LASTNAME@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(2, p.bind(a).length);
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(2);
- assertEquals("FIRSTNAME LASTNAME", p.bind(a)[0]);
- assertEquals("firstname.lastname", p.bind(a)[1]);
- assertEquals(
- "hi, FIRSTNAME LASTNAME,your eamil address is 'firstname.lastname'.right?", p.replace(a));
+ assertThat(p.bind(a)[0]).isEqualTo("FIRSTNAME LASTNAME");
+ assertThat(p.bind(a)[1]).isEqualTo("firstname.lastname");
+ assertThat(p.replace(a))
+ .isEqualTo("hi, FIRSTNAME LASTNAME,your eamil address is 'firstname.lastname'.right?");
}
@Test
public void replaceToUpperCaseToLowerCase() {
- final ParameterizedString p = new ParameterizedString("${a.toUpperCase.toLowerCase}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toUpperCase.toLowerCase}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo@example.com", p.bind(a)[0]);
- assertEquals("foo@example.com", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo@example.com");
+ assertThat(p.replace(a)).isEqualTo("foo@example.com");
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo@example.com", p.bind(a)[0]);
- assertEquals("foo@example.com", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo@example.com");
+ assertThat(p.replace(a)).isEqualTo("foo@example.com");
}
@Test
public void replaceToUpperCaseLocalName() {
- final ParameterizedString p = new ParameterizedString("${a.toUpperCase.localPart}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toUpperCase.localPart}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO", p.bind(a)[0]);
- assertEquals("FOO", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO");
+ assertThat(p.replace(a)).isEqualTo("FOO");
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO", p.bind(a)[0]);
- assertEquals("FOO", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO");
+ assertThat(p.replace(a)).isEqualTo("FOO");
}
@Test
public void replaceToUpperCaseAnUndefinedMethod() {
- final ParameterizedString p = new ParameterizedString("${a.toUpperCase.anUndefinedMethod}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toUpperCase.anUndefinedMethod}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO@EXAMPLE.COM", p.bind(a)[0]);
- assertEquals("FOO@EXAMPLE.COM", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO@EXAMPLE.COM");
+ assertThat(p.replace(a)).isEqualTo("FOO@EXAMPLE.COM");
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO@EXAMPLE.COM", p.bind(a)[0]);
- assertEquals("FOO@EXAMPLE.COM", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO@EXAMPLE.COM");
+ assertThat(p.replace(a)).isEqualTo("FOO@EXAMPLE.COM");
}
@Test
public void replaceLocalNameToUpperCase() {
- final ParameterizedString p = new ParameterizedString("${a.localPart.toUpperCase}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.localPart.toUpperCase}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO", p.bind(a)[0]);
- assertEquals("FOO", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO");
+ assertThat(p.replace(a)).isEqualTo("FOO");
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO", p.bind(a)[0]);
- assertEquals("FOO", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO");
+ assertThat(p.replace(a)).isEqualTo("FOO");
}
@Test
public void replaceLocalNameToLowerCase() {
- final ParameterizedString p = new ParameterizedString("${a.localPart.toLowerCase}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.localPart.toLowerCase}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
}
@Test
public void replaceLocalNameAnUndefinedMethod() {
- final ParameterizedString p = new ParameterizedString("${a.localPart.anUndefinedMethod}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.localPart.anUndefinedMethod}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO", p.bind(a)[0]);
- assertEquals("FOO", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO");
+ assertThat(p.replace(a)).isEqualTo("FOO");
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
}
@Test
public void replaceToLowerCaseToUpperCase() {
- final ParameterizedString p = new ParameterizedString("${a.toLowerCase.toUpperCase}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toLowerCase.toUpperCase}");
+ assertThat(p.getParameterNames()).hasSize(1);
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO@EXAMPLE.COM", p.bind(a)[0]);
- assertEquals("FOO@EXAMPLE.COM", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO@EXAMPLE.COM");
+ assertThat(p.replace(a)).isEqualTo("FOO@EXAMPLE.COM");
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("FOO@EXAMPLE.COM", p.bind(a)[0]);
- assertEquals("FOO@EXAMPLE.COM", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("FOO@EXAMPLE.COM");
+ assertThat(p.replace(a)).isEqualTo("FOO@EXAMPLE.COM");
}
@Test
public void replaceToLowerCaseLocalName() {
- final ParameterizedString p = new ParameterizedString("${a.toLowerCase.localPart}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toLowerCase.localPart}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo", p.bind(a)[0]);
- assertEquals("foo", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo");
+ assertThat(p.replace(a)).isEqualTo("foo");
}
@Test
public void replaceToLowerCaseAnUndefinedMethod() {
- final ParameterizedString p = new ParameterizedString("${a.toLowerCase.anUndefinedMethod}");
- assertEquals(1, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("a"));
+ ParameterizedString p = new ParameterizedString("${a.toLowerCase.anUndefinedMethod}");
+ assertThat(p.getParameterNames()).containsExactly("a");
- final Map<String, String> a = new HashMap<>();
+ Map<String, String> a = new HashMap<>();
a.put("a", "foo@example.com");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo@example.com", p.bind(a)[0]);
- assertEquals("foo@example.com", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo@example.com");
+ assertThat(p.replace(a)).isEqualTo("foo@example.com");
a.put("a", "FOO@EXAMPLE.COM");
- assertNotNull(p.bind(a));
- assertEquals(1, p.bind(a).length);
- assertEquals("foo@example.com", p.bind(a)[0]);
- assertEquals("foo@example.com", p.replace(a));
+ assertThat(p.bind(a)).isNotNull();
+ assertThat(p.bind(a)).hasLength(1);
+ assertThat(p.bind(a)[0]).isEqualTo("foo@example.com");
+ assertThat(p.replace(a)).isEqualTo("foo@example.com");
}
@Test
public void replaceSubmitTooltipWithVariables() {
ParameterizedString p = new ParameterizedString("Submit patch set ${patchSet} into ${branch}");
- assertEquals(2, p.getParameterNames().size());
- assertTrue(p.getParameterNames().contains("patchSet"));
+ assertThat(p.getParameterNames()).hasSize(2);
+ assertThat(p.getParameterNames()).containsExactly("patchSet", "branch");
Map<String, String> params =
ImmutableMap.of(
"patchSet", "42",
"branch", "foo");
- assertNotNull(p.bind(params));
- assertEquals(2, p.bind(params).length);
- assertEquals("42", p.bind(params)[0]);
- assertEquals("foo", p.bind(params)[1]);
- assertEquals("Submit patch set 42 into foo", p.replace(params));
+ assertThat(p.bind(params)).isNotNull();
+ assertThat(p.bind(params)).hasLength(2);
+ assertThat(p.bind(params)[0]).isEqualTo("42");
+ assertThat(p.bind(params)[1]).isEqualTo("foo");
+ assertThat(p.replace(params)).isEqualTo("Submit patch set 42 into foo");
}
@Test
@@ -411,7 +392,7 @@
ImmutableMap.of(
"patchSet", "42",
"branch", "foo");
- assertEquals(0, p.bind(params).length);
- assertEquals("Submit patch set 40 into master", p.replace(params));
+ assertThat(p.bind(params)).isEmpty();
+ assertThat(p.replace(params)).isEqualTo("Submit patch set 40 into master");
}
}
diff --git a/javatests/com/google/gerrit/common/data/PermissionTest.java b/javatests/com/google/gerrit/common/data/PermissionTest.java
new file mode 100644
index 0000000..f76323f
--- /dev/null
+++ b/javatests/com/google/gerrit/common/data/PermissionTest.java
@@ -0,0 +1,341 @@
+// 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.common.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PermissionTest {
+ private static final String PERMISSION_NAME = "foo";
+
+ private Permission permission;
+
+ @Before
+ public void setup() {
+ this.permission = new Permission(PERMISSION_NAME);
+ }
+
+ @Test
+ public void isPermission() {
+ assertThat(Permission.isPermission(Permission.ABANDON)).isTrue();
+ assertThat(Permission.isPermission("no-permission")).isFalse();
+
+ assertThat(Permission.isPermission(Permission.LABEL + "Code-Review")).isTrue();
+ assertThat(Permission.isPermission(Permission.LABEL_AS + "Code-Review")).isTrue();
+ assertThat(Permission.isPermission("Code-Review")).isFalse();
+ }
+
+ @Test
+ public void hasRange() {
+ assertThat(Permission.hasRange(Permission.ABANDON)).isFalse();
+ assertThat(Permission.hasRange("no-permission")).isFalse();
+
+ assertThat(Permission.hasRange(Permission.LABEL + "Code-Review")).isTrue();
+ assertThat(Permission.hasRange(Permission.LABEL_AS + "Code-Review")).isTrue();
+ assertThat(Permission.hasRange("Code-Review")).isFalse();
+ }
+
+ @Test
+ public void isLabel() {
+ assertThat(Permission.isLabel(Permission.ABANDON)).isFalse();
+ assertThat(Permission.isLabel("no-permission")).isFalse();
+
+ assertThat(Permission.isLabel(Permission.LABEL + "Code-Review")).isTrue();
+ assertThat(Permission.isLabel(Permission.LABEL_AS + "Code-Review")).isFalse();
+ assertThat(Permission.isLabel("Code-Review")).isFalse();
+ }
+
+ @Test
+ public void isLabelAs() {
+ assertThat(Permission.isLabelAs(Permission.ABANDON)).isFalse();
+ assertThat(Permission.isLabelAs("no-permission")).isFalse();
+
+ assertThat(Permission.isLabelAs(Permission.LABEL + "Code-Review")).isFalse();
+ assertThat(Permission.isLabelAs(Permission.LABEL_AS + "Code-Review")).isTrue();
+ assertThat(Permission.isLabelAs("Code-Review")).isFalse();
+ }
+
+ @Test
+ public void forLabel() {
+ assertThat(Permission.forLabel("Code-Review")).isEqualTo(Permission.LABEL + "Code-Review");
+ }
+
+ @Test
+ public void forLabelAs() {
+ assertThat(Permission.forLabelAs("Code-Review")).isEqualTo(Permission.LABEL_AS + "Code-Review");
+ }
+
+ @Test
+ public void extractLabel() {
+ assertThat(Permission.extractLabel(Permission.LABEL + "Code-Review")).isEqualTo("Code-Review");
+ assertThat(Permission.extractLabel(Permission.LABEL_AS + "Code-Review"))
+ .isEqualTo("Code-Review");
+ assertThat(Permission.extractLabel("Code-Review")).isNull();
+ assertThat(Permission.extractLabel(Permission.ABANDON)).isNull();
+ }
+
+ @Test
+ public void canBeOnAllProjects() {
+ assertThat(Permission.canBeOnAllProjects(AccessSection.ALL, Permission.ABANDON)).isTrue();
+ assertThat(Permission.canBeOnAllProjects(AccessSection.ALL, Permission.OWNER)).isFalse();
+ assertThat(Permission.canBeOnAllProjects(AccessSection.ALL, Permission.LABEL + "Code-Review"))
+ .isTrue();
+ assertThat(
+ Permission.canBeOnAllProjects(AccessSection.ALL, Permission.LABEL_AS + "Code-Review"))
+ .isTrue();
+
+ assertThat(Permission.canBeOnAllProjects("refs/heads/*", Permission.ABANDON)).isTrue();
+ assertThat(Permission.canBeOnAllProjects("refs/heads/*", Permission.OWNER)).isTrue();
+ assertThat(Permission.canBeOnAllProjects("refs/heads/*", Permission.LABEL + "Code-Review"))
+ .isTrue();
+ assertThat(Permission.canBeOnAllProjects("refs/heads/*", Permission.LABEL_AS + "Code-Review"))
+ .isTrue();
+ }
+
+ @Test
+ public void getName() {
+ assertThat(permission.getName()).isEqualTo(PERMISSION_NAME);
+ }
+
+ @Test
+ public void getLabel() {
+ assertThat(new Permission(Permission.LABEL + "Code-Review").getLabel())
+ .isEqualTo("Code-Review");
+ assertThat(new Permission(Permission.LABEL_AS + "Code-Review").getLabel())
+ .isEqualTo("Code-Review");
+ assertThat(new Permission("Code-Review").getLabel()).isNull();
+ assertThat(new Permission(Permission.ABANDON).getLabel()).isNull();
+ }
+
+ @Test
+ public void exclusiveGroup() {
+ assertThat(permission.getExclusiveGroup()).isFalse();
+
+ permission.setExclusiveGroup(true);
+ assertThat(permission.getExclusiveGroup()).isTrue();
+
+ permission.setExclusiveGroup(false);
+ assertThat(permission.getExclusiveGroup()).isFalse();
+ }
+
+ @Test
+ public void noExclusiveGroupOnOwnerPermission() {
+ Permission permission = new Permission(Permission.OWNER);
+ assertThat(permission.getExclusiveGroup()).isFalse();
+
+ permission.setExclusiveGroup(true);
+ assertThat(permission.getExclusiveGroup()).isFalse();
+ }
+
+ @Test
+ public void getEmptyRules() {
+ assertThat(permission.getRules()).isNotNull();
+ assertThat(permission.getRules()).isEmpty();
+ }
+
+ @Test
+ public void setAndGetRules() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
+ assertThat(permission.getRules()).containsExactly(permissionRule1, permissionRule2).inOrder();
+
+ PermissionRule permissionRule3 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-3"), "group3"));
+ permission.setRules(ImmutableList.of(permissionRule3));
+ assertThat(permission.getRules()).containsExactly(permissionRule3);
+ }
+
+ @Test
+ public void cannotAddPermissionByModifyingListThatWasProvidedToAccessSection() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+
+ List<PermissionRule> rules = new ArrayList<>();
+ rules.add(permissionRule1);
+ rules.add(permissionRule2);
+ permission.setRules(rules);
+ assertThat(permission.getRule(groupReference3)).isNull();
+
+ PermissionRule permissionRule3 = new PermissionRule(groupReference3);
+ rules.add(permissionRule3);
+ assertThat(permission.getRule(groupReference3)).isNull();
+ }
+
+ @Test
+ public void cannotAddPermissionByModifyingListThatWasRetrievedFromAccessSection() {
+ GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
+ PermissionRule permissionRule1 = new PermissionRule(groupReference1);
+ permission.getRules().add(permissionRule1);
+ assertThat(permission.getRule(groupReference1)).isNull();
+
+ List<PermissionRule> rules = new ArrayList<>();
+ rules.add(new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2")));
+ rules.add(new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-3"), "group3")));
+ permission.setRules(rules);
+ assertThat(permission.getRule(groupReference1)).isNull();
+ permission.getRules().add(permissionRule1);
+ assertThat(permission.getRule(groupReference1)).isNull();
+ }
+
+ @Test
+ public void getNonExistingRule() {
+ GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
+ assertThat(permission.getRule(groupReference)).isNull();
+ assertThat(permission.getRule(groupReference, false)).isNull();
+ }
+
+ @Test
+ public void getRule() {
+ GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
+ PermissionRule permissionRule = new PermissionRule(groupReference);
+ permission.setRules(ImmutableList.of(permissionRule));
+ assertThat(permission.getRule(groupReference)).isEqualTo(permissionRule);
+ }
+
+ @Test
+ public void createMissingRuleOnGet() {
+ GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
+ assertThat(permission.getRule(groupReference)).isNull();
+
+ assertThat(permission.getRule(groupReference, true))
+ .isEqualTo(new PermissionRule(groupReference));
+ }
+
+ @Test
+ public void addRule() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
+ GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+ assertThat(permission.getRule(groupReference3)).isNull();
+
+ PermissionRule permissionRule3 = new PermissionRule(groupReference3);
+ permission.add(permissionRule3);
+ assertThat(permission.getRule(groupReference3)).isEqualTo(permissionRule3);
+ assertThat(permission.getRules())
+ .containsExactly(permissionRule1, permissionRule2, permissionRule3)
+ .inOrder();
+ }
+
+ @Test
+ public void removeRule() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+ PermissionRule permissionRule3 = new PermissionRule(groupReference3);
+
+ permission.setRules(ImmutableList.of(permissionRule1, permissionRule2, permissionRule3));
+ assertThat(permission.getRule(groupReference3)).isNotNull();
+
+ permission.remove(permissionRule3);
+ assertThat(permission.getRule(groupReference3)).isNull();
+ assertThat(permission.getRules()).containsExactly(permissionRule1, permissionRule2).inOrder();
+ }
+
+ @Test
+ public void removeRuleByGroupReference() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+ PermissionRule permissionRule3 = new PermissionRule(groupReference3);
+
+ permission.setRules(ImmutableList.of(permissionRule1, permissionRule2, permissionRule3));
+ assertThat(permission.getRule(groupReference3)).isNotNull();
+
+ permission.removeRule(groupReference3);
+ assertThat(permission.getRule(groupReference3)).isNull();
+ assertThat(permission.getRules()).containsExactly(permissionRule1, permissionRule2).inOrder();
+ }
+
+ @Test
+ public void clearRules() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+
+ permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
+ assertThat(permission.getRules()).isNotEmpty();
+
+ permission.clearRules();
+ assertThat(permission.getRules()).isEmpty();
+ }
+
+ @Test
+ public void mergePermissions() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ PermissionRule permissionRule3 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-3"), "group3"));
+
+ Permission permission1 = new Permission("foo");
+ permission1.setRules(ImmutableList.of(permissionRule1, permissionRule2));
+
+ Permission permission2 = new Permission("bar");
+ permission2.setRules(ImmutableList.of(permissionRule2, permissionRule3));
+
+ permission1.mergeFrom(permission2);
+ assertThat(permission1.getRules())
+ .containsExactly(permissionRule1, permissionRule2, permissionRule3)
+ .inOrder();
+ }
+
+ @Test
+ public void testEquals() {
+ PermissionRule permissionRule1 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ PermissionRule permissionRule2 =
+ new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+
+ permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
+
+ Permission permissionSameRulesOtherName = new Permission("bar");
+ permissionSameRulesOtherName.setRules(ImmutableList.of(permissionRule1, permissionRule2));
+ assertThat(permission.equals(permissionSameRulesOtherName)).isFalse();
+
+ Permission permissionSameRulesSameNameOtherExclusiveGroup = new Permission("foo");
+ permissionSameRulesSameNameOtherExclusiveGroup.setRules(
+ ImmutableList.of(permissionRule1, permissionRule2));
+ permissionSameRulesSameNameOtherExclusiveGroup.setExclusiveGroup(true);
+ assertThat(permission.equals(permissionSameRulesSameNameOtherExclusiveGroup)).isFalse();
+
+ Permission permissionOther = new Permission(PERMISSION_NAME);
+ permissionOther.setRules(ImmutableList.of(permissionRule1));
+ assertThat(permission.equals(permissionOther)).isFalse();
+
+ permissionOther.add(permissionRule2);
+ assertThat(permission.equals(permissionOther)).isTrue();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index 3113a8a..f405c28 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -30,6 +30,7 @@
visibility = ["//visibility:public"],
runtime_deps = [
"//lib/bouncycastle:bcprov",
+ "//prolog:gerrit-prolog-common",
],
deps = [
":custom-truth-subjects",
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index c677be5..cc7302f 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -79,24 +79,10 @@
@Ignore
@RunWith(ConfigSuite.class)
public abstract class AbstractChangeNotesTest extends GerritBaseTests {
- @ConfigSuite.Default
- public static Config changeNotesLegacy() {
- Config cfg = new Config();
- cfg.setBoolean("notedb", null, "writeJson", false);
- return cfg;
- }
-
- @ConfigSuite.Config
- public static Config changeNotesJson() {
- Config cfg = new Config();
- cfg.setBoolean("notedb", null, "writeJson", true);
- return cfg;
- }
+ private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
@ConfigSuite.Parameter public Config testConfig;
- private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
-
protected Account.Id otherUserId;
protected FakeAccountCache accountCache;
protected IdentifiedUser changeOwner;
@@ -172,7 +158,6 @@
bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
bind(MetricMaker.class).to(DisabledMetricMaker.class);
bind(ReviewDb.class).toProvider(Providers.<ReviewDb>of(null));
-
MutableNotesMigration migration = MutableNotesMigration.newDisabled();
migration.setFrom(NotesMigrationState.FINAL);
bind(MutableNotesMigration.class).toInstance(migration);
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
index de964d8..313fa07 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
@@ -498,11 +498,7 @@
private RevCommit writeCommit(String body) throws Exception {
ChangeNoteUtil noteUtil = injector.getInstance(ChangeNoteUtil.class);
return writeCommit(
- body,
- noteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(changeOwner.getAccount(), TimeUtil.nowTs(), serverIdent),
- false);
+ body, noteUtil.newIdent(changeOwner.getAccount(), TimeUtil.nowTs(), serverIdent), false);
}
private RevCommit writeCommit(String body, PersonIdent author) throws Exception {
@@ -513,9 +509,7 @@
ChangeNoteUtil noteUtil = injector.getInstance(ChangeNoteUtil.class);
return writeCommit(
body,
- noteUtil
- .getLegacyChangeNoteWrite()
- .newIdent(changeOwner.getAccount(), TimeUtil.nowTs(), serverIdent),
+ noteUtil.newIdent(changeOwner.getAccount(), TimeUtil.nowTs(), serverIdent),
initWorkInProgress);
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 74ba0c2..bea61ed 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -58,17 +58,14 @@
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.sql.Timestamp;
-import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.notes.Note;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -79,7 +76,6 @@
@Inject private ChangeNoteJson changeNoteJson;
@Inject private LegacyChangeNoteRead legacyChangeNoteRead;
- @Inject private ChangeNoteUtil noteUtil;
@Inject private @GerritServerId String serverId;
@@ -999,7 +995,7 @@
update.commit();
try {
- notes = newNotes(c);
+ newNotes(c);
fail("Expected IOException");
} catch (OrmException e) {
assertCause(
@@ -1149,10 +1145,8 @@
update.commit();
ChangeNotes notes = newNotes(c);
- String note = readNote(notes, commit);
- if (!testJson()) {
- assertThat(note).isEqualTo(pushCert);
- }
+ readNote(notes, commit);
+
Map<PatchSet.Id, PatchSet> patchSets = notes.getPatchSets();
assertThat(patchSets.get(psId1).getPushCertificate()).isNull();
assertThat(patchSets.get(psId2).getPushCertificate()).isEqualTo(pushCert);
@@ -1185,27 +1179,6 @@
assertThat(patchSets.get(psId1).getPushCertificate()).isNull();
assertThat(patchSets.get(psId2).getPushCertificate()).isEqualTo(pushCert);
assertThat(notes.getComments()).isNotEmpty();
-
- if (!testJson()) {
- assertThat(readNote(notes, commit))
- .isEqualTo(
- pushCert
- + "Revision: "
- + commit.name()
- + "\n"
- + "Patch-set: 2\n"
- + "File: a.txt\n"
- + "\n"
- + "1:2-3:4\n"
- + NoteDbUtil.formatTime(serverIdent, ts)
- + "\n"
- + "Author: Change Owner <1@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid1\n"
- + "Bytes: 7\n"
- + "Comment\n"
- + "\n");
- }
}
@Test
@@ -1591,307 +1564,6 @@
}
@Test
- public void patchLineCommentNotesFormatSide1() throws Exception {
- Change c = newChange();
- ChangeUpdate update = newUpdate(c, otherUser);
- String uuid1 = "uuid1";
- String uuid2 = "uuid2";
- String uuid3 = "uuid3";
- String message1 = "comment 1";
- String message2 = "comment 2";
- String message3 = "comment 3";
- CommentRange range1 = new CommentRange(1, 1, 2, 1);
- Timestamp time1 = TimeUtil.nowTs();
- Timestamp time2 = TimeUtil.nowTs();
- Timestamp time3 = TimeUtil.nowTs();
- PatchSet.Id psId = c.currentPatchSetId();
-
- Comment comment1 =
- newComment(
- psId,
- "file1",
- uuid1,
- range1,
- range1.getEndLine(),
- otherUser,
- null,
- time1,
- message1,
- (short) 1,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
- false);
- update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
- update.commit();
-
- update = newUpdate(c, otherUser);
- CommentRange range2 = new CommentRange(2, 1, 3, 1);
- Comment comment2 =
- newComment(
- psId,
- "file1",
- uuid2,
- range2,
- range2.getEndLine(),
- otherUser,
- null,
- time2,
- message2,
- (short) 1,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
- false);
- update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment2);
- update.commit();
-
- update = newUpdate(c, otherUser);
- CommentRange range3 = new CommentRange(3, 0, 4, 1);
- Comment comment3 =
- newComment(
- psId,
- "file2",
- uuid3,
- range3,
- range3.getEndLine(),
- otherUser,
- null,
- time3,
- message3,
- (short) 1,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
- false);
- update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment3);
- update.commit();
-
- ChangeNotes notes = newNotes(c);
-
- try (RevWalk walk = new RevWalk(repo)) {
- ArrayList<Note> notesInTree = Lists.newArrayList(notes.revisionNoteMap.noteMap.iterator());
- Note note = Iterables.getOnlyElement(notesInTree);
-
- byte[] bytes = walk.getObjectReader().open(note.getData(), Constants.OBJ_BLOB).getBytes();
- String noteString = new String(bytes, UTF_8);
-
- if (!testJson()) {
- assertThat(noteString)
- .isEqualTo(
- "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n"
- + "Patch-set: 1\n"
- + "File: file1\n"
- + "\n"
- + "1:1-2:1\n"
- + NoteDbUtil.formatTime(serverIdent, time1)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid1\n"
- + "Bytes: 9\n"
- + "comment 1\n"
- + "\n"
- + "2:1-3:1\n"
- + NoteDbUtil.formatTime(serverIdent, time2)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid2\n"
- + "Bytes: 9\n"
- + "comment 2\n"
- + "\n"
- + "File: file2\n"
- + "\n"
- + "3:0-4:1\n"
- + NoteDbUtil.formatTime(serverIdent, time3)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid3\n"
- + "Bytes: 9\n"
- + "comment 3\n"
- + "\n");
- }
- }
- }
-
- @Test
- public void patchLineCommentNotesFormatSide0() throws Exception {
- Change c = newChange();
- ChangeUpdate update = newUpdate(c, otherUser);
- String uuid1 = "uuid1";
- String uuid2 = "uuid2";
- String message1 = "comment 1";
- String message2 = "comment 2";
- CommentRange range1 = new CommentRange(1, 1, 2, 1);
- Timestamp time1 = TimeUtil.nowTs();
- Timestamp time2 = TimeUtil.nowTs();
- PatchSet.Id psId = c.currentPatchSetId();
-
- Comment comment1 =
- newComment(
- psId,
- "file1",
- uuid1,
- range1,
- range1.getEndLine(),
- otherUser,
- null,
- time1,
- message1,
- (short) 0,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
- false);
- update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
- update.commit();
-
- update = newUpdate(c, otherUser);
- CommentRange range2 = new CommentRange(2, 1, 3, 1);
- Comment comment2 =
- newComment(
- psId,
- "file1",
- uuid2,
- range2,
- range2.getEndLine(),
- otherUser,
- null,
- time2,
- message2,
- (short) 0,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
- false);
- update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment2);
- update.commit();
-
- ChangeNotes notes = newNotes(c);
-
- try (RevWalk walk = new RevWalk(repo)) {
- ArrayList<Note> notesInTree = Lists.newArrayList(notes.revisionNoteMap.noteMap.iterator());
- Note note = Iterables.getOnlyElement(notesInTree);
-
- byte[] bytes = walk.getObjectReader().open(note.getData(), Constants.OBJ_BLOB).getBytes();
- String noteString = new String(bytes, UTF_8);
-
- if (!testJson()) {
- assertThat(noteString)
- .isEqualTo(
- "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n"
- + "Base-for-patch-set: 1\n"
- + "File: file1\n"
- + "\n"
- + "1:1-2:1\n"
- + NoteDbUtil.formatTime(serverIdent, time1)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid1\n"
- + "Bytes: 9\n"
- + "comment 1\n"
- + "\n"
- + "2:1-3:1\n"
- + NoteDbUtil.formatTime(serverIdent, time2)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid2\n"
- + "Bytes: 9\n"
- + "comment 2\n"
- + "\n");
- }
- }
- }
-
- @Test
- public void patchLineCommentNotesResolvedChangesValue() throws Exception {
- Change c = newChange();
- ChangeUpdate update = newUpdate(c, otherUser);
- String uuid1 = "uuid1";
- String uuid2 = "uuid2";
- String message1 = "comment 1";
- String message2 = "comment 2";
- CommentRange range1 = new CommentRange(1, 1, 2, 1);
- Timestamp time1 = TimeUtil.nowTs();
- Timestamp time2 = TimeUtil.nowTs();
- PatchSet.Id psId = c.currentPatchSetId();
-
- Comment comment1 =
- newComment(
- psId,
- "file1",
- uuid1,
- range1,
- range1.getEndLine(),
- otherUser,
- null,
- time1,
- message1,
- (short) 0,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
- false);
- update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
- update.commit();
-
- update = newUpdate(c, otherUser);
- Comment comment2 =
- newComment(
- psId,
- "file1",
- uuid2,
- range1,
- range1.getEndLine(),
- otherUser,
- uuid1,
- time2,
- message2,
- (short) 0,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
- true);
- update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment2);
- update.commit();
-
- ChangeNotes notes = newNotes(c);
-
- try (RevWalk walk = new RevWalk(repo)) {
- ArrayList<Note> notesInTree = Lists.newArrayList(notes.revisionNoteMap.noteMap.iterator());
- Note note = Iterables.getOnlyElement(notesInTree);
-
- byte[] bytes = walk.getObjectReader().open(note.getData(), Constants.OBJ_BLOB).getBytes();
- String noteString = new String(bytes, UTF_8);
-
- if (!testJson()) {
- assertThat(noteString)
- .isEqualTo(
- "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n"
- + "Base-for-patch-set: 1\n"
- + "File: file1\n"
- + "\n"
- + "1:1-2:1\n"
- + NoteDbUtil.formatTime(serverIdent, time1)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid1\n"
- + "Bytes: 9\n"
- + "comment 1\n"
- + "\n"
- + "1:1-2:1\n"
- + NoteDbUtil.formatTime(serverIdent, time2)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Parent: uuid1\n"
- + "Unresolved: true\n"
- + "UUID: uuid2\n"
- + "Bytes: 9\n"
- + "comment 2\n"
- + "\n");
- }
- }
- }
-
- @Test
public void patchLineCommentNotesFormatMultiplePatchSetsSameRevId() throws Exception {
Change c = newChange();
PatchSet.Id psId1 = c.currentPatchSetId();
@@ -1959,54 +1631,6 @@
update.commit();
ChangeNotes notes = newNotes(c);
-
- try (RevWalk walk = new RevWalk(repo)) {
- ArrayList<Note> notesInTree = Lists.newArrayList(notes.revisionNoteMap.noteMap.iterator());
- Note note = Iterables.getOnlyElement(notesInTree);
-
- byte[] bytes = walk.getObjectReader().open(note.getData(), Constants.OBJ_BLOB).getBytes();
- String noteString = new String(bytes, UTF_8);
- String timeStr = NoteDbUtil.formatTime(serverIdent, time);
-
- if (!testJson()) {
- assertThat(noteString)
- .isEqualTo(
- "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n"
- + "Base-for-patch-set: 1\n"
- + "File: file1\n"
- + "\n"
- + "1:1-2:1\n"
- + timeStr
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid1\n"
- + "Bytes: 9\n"
- + "comment 1\n"
- + "\n"
- + "2:1-3:1\n"
- + timeStr
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid2\n"
- + "Bytes: 9\n"
- + "comment 2\n"
- + "\n"
- + "Base-for-patch-set: 2\n"
- + "File: file1\n"
- + "\n"
- + "1:1-2:1\n"
- + timeStr
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid3\n"
- + "Bytes: 9\n"
- + "comment 3\n"
- + "\n");
- }
- }
assertThat(notes.getComments())
.isEqualTo(
ImmutableListMultimap.of(
@@ -2048,32 +1672,6 @@
ChangeNotes notes = newNotes(c);
- try (RevWalk walk = new RevWalk(repo)) {
- ArrayList<Note> notesInTree = Lists.newArrayList(notes.revisionNoteMap.noteMap.iterator());
- Note note = Iterables.getOnlyElement(notesInTree);
-
- byte[] bytes = walk.getObjectReader().open(note.getData(), Constants.OBJ_BLOB).getBytes();
- String noteString = new String(bytes, UTF_8);
-
- if (!testJson()) {
- assertThat(noteString)
- .isEqualTo(
- "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n"
- + "Patch-set: 1\n"
- + "File: file\n"
- + "\n"
- + "1:1-2:1\n"
- + NoteDbUtil.formatTime(serverIdent, time)
- + "\n"
- + "Author: Other Account <2@gerrit>\n"
- + "Real-author: Change Owner <1@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid\n"
- + "Bytes: 7\n"
- + "comment\n"
- + "\n");
- }
- }
assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(revId, comment));
}
@@ -2112,32 +1710,6 @@
ChangeNotes notes = newNotes(c);
- try (RevWalk walk = new RevWalk(repo)) {
- ArrayList<Note> notesInTree = Lists.newArrayList(notes.revisionNoteMap.noteMap.iterator());
- Note note = Iterables.getOnlyElement(notesInTree);
-
- byte[] bytes = walk.getObjectReader().open(note.getData(), Constants.OBJ_BLOB).getBytes();
- String noteString = new String(bytes, UTF_8);
- String timeStr = NoteDbUtil.formatTime(serverIdent, time);
-
- if (!testJson()) {
- assertThat(noteString)
- .isEqualTo(
- "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n"
- + "Patch-set: 1\n"
- + "File: file1\n"
- + "\n"
- + "1:1-2:1\n"
- + timeStr
- + "\n"
- + "Author: Weird\u0002User <3@gerrit>\n"
- + "Unresolved: false\n"
- + "UUID: uuid\n"
- + "Bytes: 7\n"
- + "comment\n"
- + "\n");
- }
- }
assertThat(notes.getComments())
.isEqualTo(ImmutableListMultimap.of(new RevId(comment.revId), comment));
}
@@ -2604,7 +2176,6 @@
assertThat(notes.getDraftCommentNotes().getNoteMap().contains(objId)).isTrue();
update = newUpdate(c, otherUser);
- now = TimeUtil.nowTs();
update.setPatchSetId(psId);
update.deleteComment(comment);
update.commit();
@@ -2674,7 +2245,6 @@
assertThat(notes.getDraftComments(otherUserId)).hasSize(2);
update = newUpdate(c, otherUser);
- now = TimeUtil.nowTs();
update.setPatchSetId(ps2);
update.deleteComment(comment2);
update.commit();
@@ -3525,10 +3095,6 @@
update.commit();
}
- private boolean testJson() {
- return noteUtil.getChangeNoteJson().getWriteJson();
- }
-
private String readNote(ChangeNotes notes, ObjectId noteId) throws Exception {
ObjectId dataId = notes.revisionNoteMap.noteMap.getNote(noteId).getData();
return new String(rw.getObjectReader().open(dataId, OBJ_BLOB).getCachedBytes(), UTF_8);
diff --git a/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java b/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java
new file mode 100644
index 0000000..a5c4e49
--- /dev/null
+++ b/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java
@@ -0,0 +1,535 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.update.RefUpdateUtil;
+import com.google.gerrit.testing.TestChanges;
+import com.google.inject.Inject;
+import java.io.ByteArrayOutputStream;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.notes.NoteMap;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CommentJsonMigratorTest extends AbstractChangeNotesTest {
+ private CommentJsonMigrator migrator;
+ @Inject private ChangeNoteUtil noteUtil;
+ @Inject private CommentsUtil commentsUtil;
+ @Inject private LegacyChangeNoteWrite legacyChangeNoteWrite;
+
+ private AtomicInteger uuidCounter;
+
+ @Before
+ public void setUpCounter() {
+ uuidCounter = new AtomicInteger();
+ migrator = new CommentJsonMigrator(new ChangeNoteJson(), "gerrit");
+ }
+
+ @Test
+ public void noOpIfAllCommentsAreJson() throws Exception {
+ Change c = newChange();
+ incrementPatchSet(c);
+
+ ChangeNotes notes = newNotes(c);
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ Comment ps1Comment = newComment(notes, 1, "comment on ps1");
+ update.putComment(Status.PUBLISHED, ps1Comment);
+ update.commit();
+
+ notes = newNotes(c);
+ update = newUpdate(c, changeOwner);
+ Comment ps2Comment = newComment(notes, 2, "comment on ps2");
+ update.putComment(Status.PUBLISHED, ps2Comment);
+ update.commit();
+
+ notes = newNotes(c);
+ assertThat(getToStringRepresentations(notes.getComments()))
+ .containsExactly(
+ getRevId(notes, 1), ps1Comment.toString(),
+ getRevId(notes, 2), ps2Comment.toString());
+
+ ChangeNotes oldNotes = notes;
+ migrate(project, migrator::migrateChanges, 0);
+ assertNoDifferences(notes, oldNotes);
+ assertThat(notes.getMetaId()).isEqualTo(oldNotes.getMetaId());
+ }
+
+ @Test
+ public void migratePublishedComments() throws Exception {
+ Change c = newChange();
+ incrementPatchSet(c);
+
+ ChangeNotes notes = newNotes(c);
+
+ Comment ps1Comment1 = newComment(notes, 1, "first comment on ps1");
+ Comment ps2Comment1 = newComment(notes, 2, "first comment on ps2");
+ Comment ps1Comment2 = newComment(notes, 1, "second comment on ps1");
+
+ // Construct legacy format 'by hand'.
+ ByteArrayOutputStream out1 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder().put(1, ps1Comment1).build(), out1);
+
+ ByteArrayOutputStream out2 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder().put(2, ps2Comment1).build(), out2);
+
+ ByteArrayOutputStream out3 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder()
+ .put(1, ps1Comment2)
+ .put(1, ps1Comment1)
+ .build(),
+ out3);
+
+ TestRepository<Repository> testRepository = new TestRepository<>(repo, rw);
+
+ String metaRefName = RefNames.changeMetaRef(c.getId());
+ testRepository
+ .branch(metaRefName)
+ .commit()
+ .message("Review ps 1\n\nPatch-set: 1")
+ .add(ps1Comment1.revId, out1.toString())
+ .author(serverIdent)
+ .committer(serverIdent)
+ .create();
+
+ testRepository
+ .branch(metaRefName)
+ .commit()
+ .message("Review ps 2\n\nPatch-set: 2")
+ .add(ps2Comment1.revId, out2.toString())
+ .add(ps1Comment1.revId, out3.toString())
+ .author(serverIdent)
+ .committer(serverIdent)
+ .create();
+
+ notes = newNotes(c);
+ assertThat(getToStringRepresentations(notes.getComments()))
+ .containsExactly(
+ getRevId(notes, 1), ps1Comment1.toString(),
+ getRevId(notes, 1), ps1Comment2.toString(),
+ getRevId(notes, 2), ps2Comment1.toString());
+
+ // Comments at each commit all have legacy format.
+ ImmutableList<RevCommit> oldLog = log(project, RefNames.changeMetaRef(c.getId()));
+ assertThat(oldLog).hasSize(4);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(0))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(1))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(2)))
+ .containsExactly(ps1Comment1.key, true);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(3)))
+ .containsExactly(ps1Comment1.key, true, ps1Comment2.key, true, ps2Comment1.key, true);
+
+ ChangeNotes oldNotes = notes;
+ migrate(project, migrator::migrateChanges, 1);
+
+ // Comment content is the same.
+ notes = newNotes(c);
+ assertNoDifferences(notes, oldNotes);
+ assertThat(getToStringRepresentations(notes.getComments()))
+ .containsExactly(
+ getRevId(notes, 1), ps1Comment1.toString(),
+ getRevId(notes, 1), ps1Comment2.toString(),
+ getRevId(notes, 2), ps2Comment1.toString());
+
+ // Comments at each commit all have JSON format.
+ ImmutableList<RevCommit> newLog = log(project, RefNames.changeMetaRef(c.getId()));
+ assertLogEqualExceptTrees(newLog, oldLog);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(0))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(1))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(2)))
+ .containsExactly(ps1Comment1.key, false);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(3)))
+ .containsExactly(ps1Comment1.key, false, ps1Comment2.key, false, ps2Comment1.key, false);
+ }
+
+ @Test
+ public void migrateDraftComments() throws Exception {
+ Change c = newChange();
+ incrementPatchSet(c);
+
+ ChangeNotes notes = newNotes(c);
+ ObjectId origMetaId = notes.getMetaId();
+
+ Comment ownerCommentPs1 = newComment(notes, 1, "owner comment on ps1", changeOwner);
+ Comment ownerCommentPs2 = newComment(notes, 2, "owner comment on ps2", changeOwner);
+ Comment otherCommentPs1 = newComment(notes, 1, "other user comment on ps1", otherUser);
+
+ ByteArrayOutputStream out1 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder().put(1, ownerCommentPs1).build(), out1);
+
+ ByteArrayOutputStream out2 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder().put(2, ownerCommentPs2).build(), out2);
+
+ ByteArrayOutputStream out3 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder().put(1, otherCommentPs1).build(), out3);
+
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ RevWalk allUsersRw = new RevWalk(allUsersRepo)) {
+ TestRepository<Repository> testRepository = new TestRepository<>(allUsersRepo, allUsersRw);
+
+ testRepository
+ .branch(RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()))
+ .commit()
+ .message("Review ps 1\n\nPatch-set: 1")
+ .add(ownerCommentPs1.revId, out1.toString())
+ .author(serverIdent)
+ .committer(serverIdent)
+ .create();
+
+ testRepository
+ .branch(RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()))
+ .commit()
+ .message("Review ps 1\n\nPatch-set: 2")
+ .add(ownerCommentPs2.revId, out2.toString())
+ .author(serverIdent)
+ .committer(serverIdent)
+ .create();
+
+ testRepository
+ .branch(RefNames.refsDraftComments(c.getId(), otherUser.getAccountId()))
+ .commit()
+ .message("Review ps 2\n\nPatch-set: 2")
+ .add(otherCommentPs1.revId, out3.toString())
+ .author(serverIdent)
+ .committer(serverIdent)
+ .create();
+ }
+
+ notes = newNotes(c);
+ assertThat(getToStringRepresentations(notes.getDraftComments(changeOwner.getAccountId())))
+ .containsExactly(
+ getRevId(notes, 1), ownerCommentPs1.toString(),
+ getRevId(notes, 2), ownerCommentPs2.toString());
+ assertThat(getToStringRepresentations(notes.getDraftComments(otherUser.getAccountId())))
+ .containsExactly(getRevId(notes, 1), otherCommentPs1.toString());
+
+ // Comments at each commit all have legacy format.
+ ImmutableList<RevCommit> oldOwnerLog =
+ log(allUsers, RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()));
+ assertThat(oldOwnerLog).hasSize(2);
+ assertThat(getLegacyFormatMapForDraftComments(notes, oldOwnerLog.get(0)))
+ .containsExactly(ownerCommentPs1.key, true);
+ assertThat(getLegacyFormatMapForDraftComments(notes, oldOwnerLog.get(1)))
+ .containsExactly(ownerCommentPs1.key, true, ownerCommentPs2.key, true);
+
+ ImmutableList<RevCommit> oldOtherLog =
+ log(allUsers, RefNames.refsDraftComments(c.getId(), otherUser.getAccountId()));
+ assertThat(oldOtherLog).hasSize(1);
+ assertThat(getLegacyFormatMapForDraftComments(notes, oldOtherLog.get(0)))
+ .containsExactly(otherCommentPs1.key, true);
+
+ ChangeNotes oldNotes = notes;
+ migrate(allUsers, migrator::migrateDrafts, 2);
+ assertNoDifferences(notes, oldNotes);
+
+ // Migration doesn't touch change ref.
+ assertThat(repo.exactRef(RefNames.changeMetaRef(c.getId())).getObjectId())
+ .isEqualTo(origMetaId);
+
+ // Comment content is the same.
+ notes = newNotes(c);
+ assertThat(getToStringRepresentations(notes.getDraftComments(changeOwner.getAccountId())))
+ .containsExactly(
+ getRevId(notes, 1), ownerCommentPs1.toString(),
+ getRevId(notes, 2), ownerCommentPs2.toString());
+ assertThat(getToStringRepresentations(notes.getDraftComments(otherUser.getAccountId())))
+ .containsExactly(getRevId(notes, 1), otherCommentPs1.toString());
+
+ // Comments at each commit all have JSON format.
+ ImmutableList<RevCommit> newOwnerLog =
+ log(allUsers, RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()));
+ assertLogEqualExceptTrees(newOwnerLog, oldOwnerLog);
+ assertThat(getLegacyFormatMapForDraftComments(notes, newOwnerLog.get(0)))
+ .containsExactly(ownerCommentPs1.key, false);
+ assertThat(getLegacyFormatMapForDraftComments(notes, newOwnerLog.get(1)))
+ .containsExactly(ownerCommentPs1.key, false, ownerCommentPs2.key, false);
+
+ ImmutableList<RevCommit> newOtherLog =
+ log(allUsers, RefNames.refsDraftComments(c.getId(), otherUser.getAccountId()));
+ assertLogEqualExceptTrees(newOtherLog, oldOtherLog);
+ assertThat(getLegacyFormatMapForDraftComments(notes, newOtherLog.get(0)))
+ .containsExactly(otherCommentPs1.key, false);
+ }
+
+ @Test
+ public void migrateMixOfJsonAndLegacyComments() throws Exception {
+ // 3 comments: legacy, JSON, legacy. Because adding a comment necessarily rewrites the entire
+ // note, these comments need to be on separate patch sets.
+ Change c = newChange();
+ incrementPatchSet(c);
+ incrementPatchSet(c);
+
+ ChangeNotes notes = newNotes(c);
+
+ Comment ps1Comment = newComment(notes, 1, "comment on ps1 (legacy)");
+
+ ByteArrayOutputStream out1 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder().put(1, ps1Comment).build(), out1);
+
+ TestRepository<Repository> testRepository = new TestRepository<>(repo, rw);
+
+ String metaRefName = RefNames.changeMetaRef(c.getId());
+ testRepository
+ .branch(metaRefName)
+ .commit()
+ .message("Review ps 1\n\nPatch-set: 1")
+ .add(ps1Comment.revId, out1.toString())
+ .author(serverIdent)
+ .committer(serverIdent)
+ .create();
+
+ notes = newNotes(c);
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ Comment ps2Comment = newComment(notes, 2, "comment on ps2 (JSON)");
+ update.putComment(Status.PUBLISHED, ps2Comment);
+ update.commit();
+
+ Comment ps3Comment = newComment(notes, 3, "comment on ps3 (legacy)");
+ ByteArrayOutputStream out3 = new ByteArrayOutputStream(0);
+ legacyChangeNoteWrite.buildNote(
+ ImmutableListMultimap.<Integer, Comment>builder().put(3, ps3Comment).build(), out3);
+
+ testRepository
+ .branch(metaRefName)
+ .commit()
+ .message("Review ps 3\n\nPatch-set: 3")
+ .add(ps3Comment.revId, out3.toString())
+ .author(serverIdent)
+ .committer(serverIdent)
+ .create();
+
+ notes = newNotes(c);
+ assertThat(getToStringRepresentations(notes.getComments()))
+ .containsExactly(
+ getRevId(notes, 1), ps1Comment.toString(),
+ getRevId(notes, 2), ps2Comment.toString(),
+ getRevId(notes, 3), ps3Comment.toString());
+
+ // Comments at each commit match expected format.
+ ImmutableList<RevCommit> oldLog = log(project, RefNames.changeMetaRef(c.getId()));
+ assertThat(oldLog).hasSize(6);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(0))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(1))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(2))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(3)))
+ .containsExactly(ps1Comment.key, true);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(4)))
+ .containsExactly(ps1Comment.key, true, ps2Comment.key, false);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(5)))
+ .containsExactly(ps1Comment.key, true, ps2Comment.key, false, ps3Comment.key, true);
+
+ ChangeNotes oldNotes = notes;
+ migrate(project, migrator::migrateChanges, 1);
+ assertNoDifferences(notes, oldNotes);
+
+ // Comment content is the same.
+ notes = newNotes(c);
+ assertThat(getToStringRepresentations(notes.getComments()))
+ .containsExactly(
+ getRevId(notes, 1), ps1Comment.toString(),
+ getRevId(notes, 2), ps2Comment.toString(),
+ getRevId(notes, 3), ps3Comment.toString());
+
+ // Comments at each commit all have JSON format.
+ ImmutableList<RevCommit> newLog = log(project, RefNames.changeMetaRef(c.getId()));
+ assertLogEqualExceptTrees(newLog, oldLog);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(0))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(1))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(2))).isEmpty();
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(3)))
+ .containsExactly(ps1Comment.key, false);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(4)))
+ .containsExactly(ps1Comment.key, false, ps2Comment.key, false);
+ assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(5)))
+ .containsExactly(ps1Comment.key, false, ps2Comment.key, false, ps3Comment.key, false);
+ }
+
+ @FunctionalInterface
+ interface MigrateFunction {
+ boolean call(
+ Project.NameKey project,
+ Repository repo,
+ RevWalk rw,
+ ObjectInserter ins,
+ BatchRefUpdate bru)
+ throws Exception;
+ }
+
+ private void migrate(Project.NameKey project, MigrateFunction func, int expectedCommands)
+ throws Exception {
+ try (Repository repo = repoManager.openRepository(project);
+ RevWalk rw = new RevWalk(repo);
+ ObjectInserter ins = repo.newObjectInserter()) {
+ BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
+ bru.setAllowNonFastForwards(true);
+ assertThat(func.call(project, repo, rw, ins, bru)).isTrue();
+ assertThat(bru.getCommands()).hasSize(expectedCommands);
+ if (!bru.getCommands().isEmpty()) {
+ ins.flush();
+ RefUpdateUtil.executeChecked(bru, rw);
+ }
+ }
+ }
+
+ private Comment newComment(ChangeNotes notes, int psNum, String message) {
+ return newComment(notes, psNum, message, changeOwner);
+ }
+
+ private Comment newComment(
+ ChangeNotes notes, int psNum, String message, IdentifiedUser commenter) {
+ return newComment(
+ new PatchSet.Id(notes.getChangeId(), psNum),
+ "filename",
+ "uuid-" + uuidCounter.getAndIncrement(),
+ null,
+ 0,
+ commenter,
+ null,
+ TimeUtil.nowTs(),
+ message,
+ (short) 1,
+ getRevId(notes, psNum).get(),
+ false);
+ }
+
+ private void incrementPatchSet(Change c) throws Exception {
+ TestChanges.incrementPatchSet(c);
+ RevCommit commit = tr.commit().message("PS" + c.currentPatchSetId().get()).create();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ update.setCommit(rw, commit);
+ update.commit();
+ }
+
+ private static RevId getRevId(ChangeNotes notes, int psNum) {
+ PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), psNum);
+ PatchSet ps = notes.getPatchSets().get(psId);
+ checkArgument(ps != null, "no patch set %s: %s", psNum, notes.getPatchSets());
+ return ps.getRevision();
+ }
+
+ private static ListMultimap<RevId, String> getToStringRepresentations(
+ ListMultimap<RevId, Comment> comments) {
+ // Use string representation for equality comparison in this test, because Comment#equals only
+ // compares keys.
+ return Multimaps.transformValues(comments, Comment::toString);
+ }
+
+ private ImmutableMap<Comment.Key, Boolean> getLegacyFormatMapForPublishedComments(
+ ChangeNotes notes, ObjectId metaId) throws Exception {
+ return getLegacyFormatMap(project, notes.getChangeId(), metaId, Status.PUBLISHED);
+ }
+
+ private ImmutableMap<Comment.Key, Boolean> getLegacyFormatMapForDraftComments(
+ ChangeNotes notes, ObjectId metaId) throws Exception {
+ return getLegacyFormatMap(allUsers, notes.getChangeId(), metaId, Status.DRAFT);
+ }
+
+ private ImmutableMap<Comment.Key, Boolean> getLegacyFormatMap(
+ Project.NameKey project, Change.Id changeId, ObjectId metaId, Status status)
+ throws Exception {
+ try (Repository repo = repoManager.openRepository(project);
+ ObjectReader reader = repo.newObjectReader();
+ RevWalk rw = new RevWalk(reader)) {
+ NoteMap noteMap = NoteMap.read(reader, rw.parseCommit(metaId));
+ RevisionNoteMap<ChangeRevisionNote> revNoteMap =
+ RevisionNoteMap.parse(
+ noteUtil.getChangeNoteJson(),
+ noteUtil.getLegacyChangeNoteRead(),
+ changeId,
+ reader,
+ noteMap,
+ status);
+ return revNoteMap
+ .revisionNotes
+ .values()
+ .stream()
+ .flatMap(crn -> crn.getComments().stream())
+ .collect(toImmutableMap(c -> c.key, c -> c.legacyFormat));
+ }
+ }
+
+ private ImmutableList<RevCommit> log(Project.NameKey project, String refName) throws Exception {
+ try (Repository repo = repoManager.openRepository(project);
+ RevWalk rw = new RevWalk(repo)) {
+ rw.sort(RevSort.TOPO);
+ rw.sort(RevSort.REVERSE);
+ Ref ref = repo.exactRef(refName);
+ checkArgument(ref != null, "missing ref: %s", refName);
+ rw.markStart(rw.parseCommit(ref.getObjectId()));
+ return ImmutableList.copyOf(rw);
+ }
+ }
+
+ private static void assertLogEqualExceptTrees(
+ ImmutableList<RevCommit> actualLog, ImmutableList<RevCommit> expectedLog) {
+ assertThat(actualLog).hasSize(expectedLog.size());
+ for (int i = 0; i < expectedLog.size(); i++) {
+ RevCommit actual = actualLog.get(i);
+ RevCommit expected = expectedLog.get(i);
+ assertThat(actual.getAuthorIdent())
+ .named("author of entry %s", i)
+ .isEqualTo(expected.getAuthorIdent());
+ assertThat(actual.getCommitterIdent())
+ .named("committer of entry %s", i)
+ .isEqualTo(expected.getCommitterIdent());
+ assertThat(actual.getFullMessage()).named("message of entry %s", i).isNotNull();
+ assertThat(actual.getFullMessage())
+ .named("message of entry %s", i)
+ .isEqualTo(expected.getFullMessage());
+ }
+ }
+
+ private void assertNoDifferences(ChangeNotes actual, ChangeNotes expected) throws Exception {
+ assertThat(
+ ChangeBundle.fromNotes(commentsUtil, actual)
+ .differencesFrom(ChangeBundle.fromNotes(commentsUtil, expected)))
+ .isEmpty();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/rules/GerritCommonTest.java b/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
index 314941e..086dd65 100644
--- a/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
+++ b/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
@@ -49,7 +49,7 @@
bind(PrologEnvironment.Args.class)
.toInstance(
new PrologEnvironment.Args(
- null, null, null, null, null, null, null, cfg, null));
+ null, null, null, null, null, null, null, cfg, null, null));
}
});
}
diff --git a/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java b/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
index f709f55..8622b32 100644
--- a/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
+++ b/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
@@ -30,18 +30,18 @@
@Test
public void labelWithSpacesIsTransformed() {
assertThat(PrologRuleEvaluator.checkLabelName("Label with spaces"))
- .isEqualTo("Invalid-Prolog-Rules-Label-Name--Labelwithspaces");
+ .isEqualTo("Invalid-Prolog-Rules-Label-Name-Labelwithspaces");
}
@Test
public void labelStartingWithADashIsTransformed() {
assertThat(PrologRuleEvaluator.checkLabelName("-dashed-label"))
- .isEqualTo("Invalid-Prolog-Rules-Label-Name---dashed-label");
+ .isEqualTo("Invalid-Prolog-Rules-Label-Name-dashed-label");
}
@Test
public void labelWithInvalidCharactersIsTransformed() {
assertThat(PrologRuleEvaluator.checkLabelName("*urgent*"))
- .isEqualTo("Invalid-Prolog-Rules-Label-Name--urgent");
+ .isEqualTo("Invalid-Prolog-Rules-Label-Name-urgent");
}
}
diff --git a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index ed94c97..c4844b1 100644
--- a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.GerritServerId;
+import com.google.gerrit.server.config.GerritServerIdProvider;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.group.SystemGroupBackend;
@@ -94,7 +95,11 @@
Config cfg = new Config();
cfg.setString("user", null, "name", "Gerrit Code Review");
cfg.setString("user", null, "email", "gerrit@localhost");
-
+ cfg.setString(
+ GerritServerIdProvider.SECTION,
+ null,
+ GerritServerIdProvider.KEY,
+ "1234567");
bind(Config.class) //
.annotatedWith(GerritServerConfig.class) //
.toInstance(cfg);
diff --git a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior.html b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior.html
new file mode 100644
index 0000000..68000bc
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior.html
@@ -0,0 +1,75 @@
+<!--
+@license
+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.
+-->
+<script>
+(function(window) {
+ 'use strict';
+
+ window.Gerrit = window.Gerrit || {};
+
+ /** @polymerBehavior Gerrit.SafeTypes */
+ Gerrit.SafeTypes = {};
+
+ const SAFE_URL_PATTERN = /^(https?:\/\/|mailto:|\/|#)/i;
+
+ /**
+ * Wraps a string to be used as a URL. An error is thrown if the string cannot
+ * be considered safe.
+ * @constructor
+ * @param {string} url the unwrapped, potentially unsafe URL.
+ */
+ Gerrit.SafeTypes.SafeUrl = function(url) {
+ if (!SAFE_URL_PATTERN.test(url)) {
+ throw new Error(`URL not marked as safe: ${url}`);
+ }
+ this._url = url;
+ };
+
+ /**
+ * Get the string representation of the safe URL.
+ * @returns {string}
+ */
+ Gerrit.SafeTypes.SafeUrl.prototype.asString = function() {
+ return this._url;
+ };
+
+ Gerrit.SafeTypes.safeTypesBridge = function(value, type) {
+ // If the value is being bound to a URL, ensure the value is wrapped in the
+ // SafeUrl type first. If the URL is not safe, allow the SafeUrl constructor
+ // to surface the error.
+ if (type === 'URL') {
+ let safeValue = null;
+ if (value instanceof Gerrit.SafeTypes.SafeUrl) {
+ safeValue = value;
+ } else if (typeof value === 'string') {
+ safeValue = new Gerrit.SafeTypes.SafeUrl(value);
+ }
+ if (safeValue) {
+ return safeValue.asString();
+ }
+ }
+
+ // If the value is being bound to a string or a constant, then the string
+ // can be used as is.
+ if (type === 'STRING' || type === 'CONSTANT') {
+ return value;
+ }
+
+ // Otherwise fail.
+ throw new Error(`Refused to bind value as ${type}: ${value}`);
+ };
+})(window);
+</script>
diff --git a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
new file mode 100644
index 0000000..bc16b39
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<!--
+@license
+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.
+-->
+
+<title>safe-types-behavior</title>
+
+<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
+<script src="../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
+<link rel="import" href="safe-types-behavior.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <safe-types-element></safe-types-element>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-tooltip-behavior tests', () => {
+ let element;
+ let sandbox;
+
+ suiteSetup(() => {
+ Polymer({
+ is: 'safe-types-element',
+ behaviors: [Gerrit.SafeTypes],
+ });
+ });
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('SafeUrl accepts valid urls', () => {
+ function accepts(url) {
+ const safeUrl = new element.SafeUrl(url);
+ assert.isOk(safeUrl);
+ assert.equal(url, safeUrl.asString());
+ }
+ accepts('http://www.google.com/');
+ accepts('https://www.google.com/');
+ accepts('HtTpS://www.google.com/');
+ accepts('//www.google.com/');
+ accepts('/c/1234/file/path.html@45');
+ accepts('#hash-url');
+ accepts('mailto:name@example.com');
+ });
+
+ test('SafeUrl rejects invalid urls', () => {
+ function rejects(url) {
+ assert.throws(() => { new element.SafeUrl(url); });
+ }
+ rejects('javascript://alert("evil");');
+ rejects('ftp:example.com');
+ rejects('data:text/html,scary business');
+ });
+
+ suite('safeTypesBridge', () => {
+ function acceptsString(value, type) {
+ assert.equal(Gerrit.SafeTypes.safeTypesBridge(value, type),
+ value);
+ }
+
+ function rejects(value, type) {
+ assert.throws(() => { Gerrit.SafeTypes.safeTypesBridge(value, type); });
+ }
+
+ test('accepts valid URL strings', () => {
+ acceptsString('/foo/bar', 'URL');
+ acceptsString('#baz', 'URL');
+ });
+
+ test('rejects invalid URL strings', () => {
+ rejects('javascript://void();', 'URL');
+ });
+
+ test('accepts SafeUrl values', () => {
+ const url = '/abc/123';
+ const safeUrl = new element.SafeUrl(url);
+ assert.equal(Gerrit.SafeTypes.safeTypesBridge(safeUrl, 'URL'), url);
+ });
+
+ test('rejects non-string or non-SafeUrl types', () => {
+ rejects(3.1415926, 'URL');
+ });
+
+ test('accepts any binding to STRING or CONSTANT', () => {
+ acceptsString('foo/bar/baz', 'STRING');
+ acceptsString('lorem ipsum dolor', 'CONSTANT');
+ });
+
+ test('rejects all other types', () => {
+ rejects('foo', 'JAVASCRIPT');
+ rejects('foo', 'HTML');
+ rejects('foo', 'RESOURCE_URL');
+ rejects('foo', 'STYLE');
+ });
+ });
+ });
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
index 1893536..b99125c 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
@@ -43,7 +43,7 @@
const ForcePushOptions = {
ALLOW: [
- {name: 'Allow pushing but not force pushing', value: false},
+ {name: 'Allow pushing (but not force pushing)', value: false},
{name: 'Allow pushing with or without force', value: true},
],
BLOCK: [
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
index 556568b..f85c2b2 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
@@ -52,7 +52,7 @@
() => {
const ForcePushOptions = {
ALLOW: [
- {name: 'Allow pushing but not force pushing', value: false},
+ {name: 'Allow pushing (but not force pushing)', value: false},
{name: 'Allow pushing with or without force', value: true},
],
BLOCK: [
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index dc41f59..9930bf5 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -21,6 +21,8 @@
const CLOSED_STATUS = ['MERGED', 'ABANDONED'];
+ const LABEL_PREFIX_INVALID_PROLOG = 'Invalid-Prolog-Rules-Label-Name--';
+
Polymer({
is: 'gr-change-list',
@@ -180,7 +182,11 @@
},
_computeLabelShortcut(labelName) {
+ if (labelName.startsWith(LABEL_PREFIX_INVALID_PROLOG)) {
+ labelName = labelName.slice(LABEL_PREFIX_INVALID_PROLOG.length);
+ }
return labelName.split('-').reduce((a, i) => {
+ if (!i) { return a; }
return a + i[0].toUpperCase();
}, '');
},
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
index 3dd8c9d..9ce5764 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
@@ -127,7 +127,11 @@
assert.equal(element._computeLabelShortcut('PolyGerrit-Review'), 'PR');
assert.equal(element._computeLabelShortcut('polygerrit-review'), 'PR');
assert.equal(element._computeLabelShortcut(
+ 'Invalid-Prolog-Rules-Label-Name--Verified'), 'V');
+ assert.equal(element._computeLabelShortcut(
'Some-Special-Label-7'), 'SSL7');
+ assert.equal(element._computeLabelShortcut('--Too----many----dashes---'),
+ 'TMD');
});
test('colspans', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
index c0a09f3..234d903 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
@@ -78,7 +78,7 @@
assert.isOk(element._computeShowWebLink(element.change,
element.commitInfo, element.serverConfig));
assert.equal(element._computeWebLink(element.change, element.commitInfo,
- element.serverConfig), '../../link-url');
+ element.serverConfig), 'link-url');
});
test('does not relativize web links that begin with scheme', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
index ad14a18..299508b 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
@@ -30,6 +30,12 @@
p {
margin-bottom: 1em;
}
+ @media screen and (max-width: 50em) {
+ #dialog {
+ min-width: inherit;
+ width: 100%;
+ }
+ }
</style>
<gr-confirm-dialog
id="dialog"
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index e601b93..3e886d5 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -20,7 +20,8 @@
// Latency reporting constants.
const TIMING = {
TYPE: 'timing-report',
- CATEGORY: 'UI Latency',
+ CATEGORY_UI_LATENCY: 'UI Latency',
+ CATEGORY_RPC: 'RPC Timing',
// Reported events - alphabetize below.
APP_STARTED: 'App Started',
PAGE_LOADED: 'Page Loaded',
@@ -170,7 +171,16 @@
report.apply(this, args);
},
- defaultReporter(type, category, eventName, eventValue) {
+ /**
+ * The default reporter reports events immediately.
+ * @param {string} type
+ * @param {string} category
+ * @param {string} eventName
+ * @param {string|number} eventValue
+ * @param {boolean|undefined} opt_noLog If true, the event will not be
+ * logged to the JS console.
+ */
+ defaultReporter(type, category, eventName, eventValue, opt_noLog) {
const detail = {
type,
category,
@@ -178,6 +188,7 @@
value: eventValue,
};
document.dispatchEvent(new CustomEvent(type, {detail}));
+ if (opt_noLog) { return; }
if (type === ERROR.TYPE) {
console.error(eventValue.error || eventName);
} else {
@@ -186,7 +197,17 @@
}
},
- cachingReporter(type, category, eventName, eventValue) {
+ /**
+ * The caching reporter will queue reports until plugins have loaded, and
+ * log events immediately if they're reported after plugins have loaded.
+ * @param {string} type
+ * @param {string} category
+ * @param {string} eventName
+ * @param {string|number} eventValue
+ * @param {boolean|undefined} opt_noLog If true, the event will not be
+ * logged to the JS console.
+ */
+ cachingReporter(type, category, eventName, eventValue, opt_noLog) {
if (type === ERROR.TYPE) {
console.error(eventValue.error || eventName);
}
@@ -196,9 +217,9 @@
this.reporter(...args);
}
}
- this.reporter(type, category, eventName, eventValue);
+ this.reporter(type, category, eventName, eventValue, opt_noLog);
} else {
- pending.push([type, category, eventName, eventValue]);
+ pending.push([type, category, eventName, eventValue, opt_noLog]);
}
},
@@ -208,8 +229,8 @@
appStarted(hidden) {
const startTime =
new Date().getTime() - this.performanceTiming.navigationStart;
- this.reporter(
- TIMING.TYPE, TIMING.CATEGORY, TIMING.APP_STARTED, startTime);
+ this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+ TIMING.APP_STARTED, startTime);
if (hidden) {
this.reporter(PAGE_VISIBILITY.TYPE, PAGE_VISIBILITY.CATEGORY,
PAGE_VISIBILITY.STARTED_HIDDEN);
@@ -226,8 +247,8 @@
} else {
const loadTime = this.performanceTiming.loadEventEnd -
this.performanceTiming.navigationStart;
- this.reporter(
- TIMING.TYPE, TIMING.CATEGORY, TIMING.PAGE_LOADED, loadTime);
+ this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+ TIMING.PAGE_LOADED, loadTime);
}
},
@@ -344,7 +365,8 @@
* @param {number} time The time to report as an integer of milliseconds.
*/
_reportTiming(name, time) {
- this.reporter(TIMING.TYPE, TIMING.CATEGORY, name, Math.round(time));
+ this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY, name,
+ Math.round(time));
},
/**
@@ -395,6 +417,16 @@
return timer.reset();
},
+ /**
+ * Log timing information for an RPC.
+ * @param {string} anonymizedUrl The URL of the RPC with tokens obfuscated.
+ * @param {number} elapsed The time elapsed of the RPC.
+ */
+ reportRpcTiming(anonymizedUrl, elapsed) {
+ this.reporter(TIMING.TYPE, TIMING.CATEGORY_RPC, 'RPC-' + anonymizedUrl,
+ elapsed, true);
+ },
+
reportInteraction(eventName, opt_msg) {
this.reporter(INTERACTION_TYPE, this.category, eventName, opt_msg);
},
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
index e78f49fb..8b85074 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
@@ -264,7 +264,7 @@
sandbox.stub(element, 'now').returns(42);
element.pluginsLoaded();
assert.isTrue(element.defaultReporter.calledWithExactly(
- 'timing-report', 'UI Latency', 'PluginsLoaded', 42
+ 'timing-report', 'UI Latency', 'PluginsLoaded', 42, undefined
));
});
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 6adc286..3b4f1ec 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -309,12 +309,7 @@
if (!weblinks) { return null; }
const weblink = weblinks.find(this._isDirectCommit);
if (!weblink) { return null; }
- const url = weblink.url;
- if (url.startsWith('https:') || url.startsWith('http:')) {
- return url;
- } else {
- return `../../${url}`;
- }
+ return weblink.url;
},
_getChangeWeblinks({repo, commit, options: {weblinks}}) {
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index efcefe2..e7bd965 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -30,10 +30,12 @@
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/polymer-resin/standalone/polymer-resin.html">
+<link rel="import" href="../behaviors/safe-types-behavior/safe-types-behavior.html">
<script>
security.polymer_resin.install({
allowedIdentifierPrefixes: [''],
reportHandler: security.polymer_resin.CONSOLE_LOGGING_REPORT_HANDLER,
+ safeTypesBridge: Gerrit.SafeTypes.safeTypesBridge,
});
</script>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 7acb680..b4aec99 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -99,6 +99,7 @@
'page-error': '_handlePageError',
'title-change': '_handleTitleChange',
'location-change': '_handleLocationChange',
+ 'rpc-log': '_handleRpcLog',
},
observers: [
@@ -332,5 +333,15 @@
console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`);
console.groupEnd();
},
+
+ /**
+ * Intercept RPC log events emitted by REST API interfaces.
+ * Note: the REST API interface cannot use gr-reporting directly because
+ * that would create a cyclic dependency.
+ */
+ _handleRpcLog(e) {
+ this.$.reporting.reportRpcTiming(e.detail.anonymizedUrl,
+ e.detail.elapsed);
+ },
});
})();
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 5d68e01..fa1ad11 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
@@ -28,6 +28,15 @@
Defs.patchRange;
/**
+ * @typedef {{
+ * url: string,
+ * fetchOptions: (Object|null|undefined),
+ * anonymizedUrl: (string|undefined),
+ * }}
+ */
+ Defs.FetchRequest;
+
+ /**
* Object to describe a request for passing into _fetchJSON or _fetchRawJSON.
* - url is the URL for the request (excluding get params)
* - errFn is a function to invoke when the request fails.
@@ -40,6 +49,8 @@
* cancelCondition: (function()|null|undefined),
* params: (Object|null|undefined),
* fetchOptions: (Object|null|undefined),
+ * anonymizedUrl: (string|undefined),
+ * reportUrlAsIs: (boolean|undefined),
* }}
*/
Defs.FetchJSONRequest;
@@ -53,6 +64,8 @@
* cancelCondition: (function()|null|undefined),
* params: (Object|null|undefined),
* fetchOptions: (Object|null|undefined),
+ * anonymizedEndpoint: (string|undefined),
+ * reportEndpointAsIs: (boolean|undefined),
* }}
*/
Defs.ChangeFetchRequest;
@@ -78,6 +91,8 @@
* contentType: (string|null|undefined),
* headers: (Object|undefined),
* parseResponse: (boolean|undefined),
+ * anonymizedUrl: (string|undefined),
+ * reportUrlAsIs: (boolean|undefined),
* }}
*/
Defs.SendRequest;
@@ -93,6 +108,8 @@
* contentType: (string|null|undefined),
* headers: (Object|undefined),
* parseResponse: (boolean|undefined),
+ * anonymizedEndpoint: (string|undefined),
+ * reportEndpointAsIs: (boolean|undefined),
* }}
*/
Defs.ChangeSendRequest;
@@ -115,6 +132,9 @@
'Saving draft resulted in HTTP 200 (OK) but expected HTTP 201 (Created)';
const HEADER_REPORTING_BLACKLIST = /^set-cookie$/i;
+ const ANONYMIZED_CHANGE_BASE_URL = '/changes/*~*';
+ const ANONYMIZED_REVISION_BASE_URL = ANONYMIZED_CHANGE_BASE_URL +
+ '/revisions/*';
Polymer({
is: 'gr-rest-api-interface',
@@ -143,6 +163,12 @@
* @event auth-error
*/
+ /**
+ * Fired after an RPC completes.
+ *
+ * @event rpc-log
+ */
+
properties: {
_cache: {
type: Object,
@@ -182,15 +208,14 @@
/**
* Wraps calls to the underlying authenticated fetch function (_auth.fetch)
* with timing and logging.
- * @param {string} url
- * @param {Object=} opt_fetchOptions
+ * @param {Defs.FetchRequest} req
*/
- _fetch(url, opt_fetchOptions) {
+ _fetch(req) {
const start = Date.now();
- const xhr = this._auth.fetch(url, opt_fetchOptions);
+ const xhr = this._auth.fetch(req.url, req.fetchOptions);
// Log the call after it completes.
- xhr.then(res => this._logCall(url, opt_fetchOptions, start, res.status));
+ xhr.then(res => this._logCall(req, start, res.status));
// Return the XHR directly (without the log).
return xhr;
@@ -200,18 +225,27 @@
* Log information about a REST call. Because the elapsed time is determined
* by this method, it should be called immediately after the request
* finishes.
- * @param {string} url
- * @param {Object|undefined} fetchOptions
+ * @param {Defs.FetchRequest} req
* @param {number} startTime the time that the request was started.
* @param {number} status the HTTP status of the response. The status value
* is used here rather than the response object so there is no way this
* method can read the body stream.
*/
- _logCall(url, fetchOptions, startTime, status) {
- const method = (fetchOptions && fetchOptions.method) ?
- fetchOptions.method : 'GET';
- const elapsed = (Date.now() - startTime) + 'ms';
- console.log(['HTTP', status, method, elapsed, url].join(' '));
+ _logCall(req, startTime, status) {
+ const method = (req.fetchOptions && req.fetchOptions.method) ?
+ req.fetchOptions.method : 'GET';
+ const elapsed = (Date.now() - startTime);
+ console.log([
+ 'HTTP',
+ status,
+ method,
+ elapsed + 'ms',
+ req.anonymizedUrl || req.url,
+ ].join(' '));
+ if (req.anonymizedUrl) {
+ this.fire('rpc-log',
+ {status, method, elapsed, anonymizedUrl: req.anonymizedUrl});
+ }
},
/**
@@ -223,7 +257,12 @@
*/
_fetchRawJSON(req) {
const urlWithParams = this._urlWithParams(req.url, req.params);
- return this._fetch(urlWithParams, req.fetchOptions).then(res => {
+ const fetchReq = {
+ url: urlWithParams,
+ fetchOptions: req.fetchOptions,
+ anonymizedUrl: req.reportUrlAsIs ? urlWithParams : req.anonymizedUrl,
+ };
+ return this._fetch(fetchReq).then(res => {
if (req.cancelCondition && req.cancelCondition()) {
res.body.cancel();
return;
@@ -324,10 +363,16 @@
getConfig(noCache) {
if (!noCache) {
- return this._fetchSharedCacheURL({url: '/config/server/info'});
+ return this._fetchSharedCacheURL({
+ url: '/config/server/info',
+ reportUrlAsIs: true,
+ });
}
- return this._fetchJSON({url: '/config/server/info'});
+ return this._fetchJSON({
+ url: '/config/server/info',
+ reportUrlAsIs: true,
+ });
},
getRepo(repo, opt_errFn) {
@@ -336,6 +381,7 @@
return this._fetchSharedCacheURL({
url: '/projects/' + encodeURIComponent(repo),
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*',
});
},
@@ -345,6 +391,7 @@
return this._fetchSharedCacheURL({
url: '/projects/' + encodeURIComponent(repo) + '/config',
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/config',
});
},
@@ -353,6 +400,7 @@
// supports it.
return this._fetchSharedCacheURL({
url: '/access/?project=' + encodeURIComponent(repo),
+ anonymizedUrl: '/access/?project=*',
});
},
@@ -362,6 +410,7 @@
return this._fetchSharedCacheURL({
url: `/projects/${encodeURIComponent(repo)}/dashboards?inherited`,
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/dashboards?inherited',
});
},
@@ -374,6 +423,7 @@
url: `/projects/${encodeName}/config`,
body: config,
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/config',
});
},
@@ -387,6 +437,7 @@
url: `/projects/${encodeName}/gc`,
body: '',
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/gc',
});
},
@@ -404,6 +455,7 @@
url: `/projects/${encodeName}`,
body: config,
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*',
});
},
@@ -419,6 +471,7 @@
url: `/groups/${encodeName}`,
body: config,
errFn: opt_errFn,
+ anonymizedUrl: '/groups/*',
});
},
@@ -426,6 +479,7 @@
return this._fetchJSON({
url: `/groups/${encodeURIComponent(group)}/detail`,
errFn: opt_errFn,
+ anonymizedUrl: '/groups/*/detail',
});
},
@@ -445,6 +499,7 @@
url: `/projects/${encodeName}/branches/${encodeRef}`,
body: '',
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/branches/*',
});
},
@@ -464,6 +519,7 @@
url: `/projects/${encodeName}/tags/${encodeRef}`,
body: '',
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/tags/*',
});
},
@@ -484,6 +540,7 @@
url: `/projects/${encodeName}/branches/${encodeBranch}`,
body: revision,
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/branches/*',
});
},
@@ -504,6 +561,7 @@
url: `/projects/${encodeName}/tags/${encodeTag}`,
body: revision,
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/tags/*',
});
},
@@ -513,7 +571,11 @@
*/
getIsGroupOwner(groupName) {
const encodeName = encodeURIComponent(groupName);
- return this._fetchSharedCacheURL({url: `/groups/?owned&q=${encodeName}`})
+ const req = {
+ url: `/groups/?owned&q=${encodeName}`,
+ anonymizedUrl: '/groups/owned&q=*',
+ };
+ return this._fetchSharedCacheURL(req)
.then(configs => configs.hasOwnProperty(groupName));
},
@@ -522,12 +584,15 @@
return this._fetchJSON({
url: `/groups/${encodeName}/members/`,
errFn: opt_errFn,
+ anonymizedUrl: '/groups/*/members',
});
},
getIncludedGroup(groupName) {
- const encodeName = encodeURIComponent(groupName);
- return this._fetchJSON({url: `/groups/${encodeName}/groups/`});
+ return this._fetchJSON({
+ url: `/groups/${encodeURIComponent(groupName)}/groups/`,
+ anonymizedUrl: '/groups/*/groups',
+ });
},
saveGroupName(groupId, name) {
@@ -536,6 +601,7 @@
method: 'PUT',
url: `/groups/${encodeId}/name`,
body: {name},
+ anonymizedUrl: '/groups/*/name',
});
},
@@ -545,6 +611,7 @@
method: 'PUT',
url: `/groups/${encodeId}/owner`,
body: {owner: ownerId},
+ anonymizedUrl: '/groups/*/owner',
});
},
@@ -554,6 +621,7 @@
method: 'PUT',
url: `/groups/${encodeId}/description`,
body: {description},
+ anonymizedUrl: '/groups/*/description',
});
},
@@ -563,6 +631,7 @@
method: 'PUT',
url: `/groups/${encodeId}/options`,
body: options,
+ anonymizedUrl: '/groups/*/options',
});
},
@@ -570,6 +639,7 @@
return this._fetchSharedCacheURL({
url: '/groups/' + group + '/log.audit',
errFn: opt_errFn,
+ anonymizedUrl: '/groups/*/log.audit',
});
},
@@ -580,6 +650,7 @@
method: 'PUT',
url: `/groups/${encodeName}/members/${encodeMember}`,
parseResponse: true,
+ anonymizedUrl: '/groups/*/members/*',
});
},
@@ -590,6 +661,7 @@
method: 'PUT',
url: `/groups/${encodeName}/groups/${encodeIncludedGroup}`,
errFn: opt_errFn,
+ anonymizedUrl: '/groups/*/groups/*',
};
return this._send(req).then(response => {
if (response.ok) {
@@ -604,6 +676,7 @@
return this._send({
method: 'DELETE',
url: `/groups/${encodeName}/members/${encodeMember}`,
+ anonymizedUrl: '/groups/*/members/*',
});
},
@@ -613,11 +686,15 @@
return this._send({
method: 'DELETE',
url: `/groups/${encodeName}/groups/${encodeIncludedGroup}`,
+ anonymizedUrl: '/groups/*/groups/*',
});
},
getVersion() {
- return this._fetchSharedCacheURL({url: '/config/server/version'});
+ return this._fetchSharedCacheURL({
+ url: '/config/server/version',
+ reportUrlAsIs: true,
+ });
},
getDiffPreferences() {
@@ -625,6 +702,7 @@
if (loggedIn) {
return this._fetchSharedCacheURL({
url: '/accounts/self/preferences.diff',
+ reportUrlAsIs: true,
});
}
// These defaults should match the defaults in
@@ -655,6 +733,7 @@
if (loggedIn) {
return this._fetchSharedCacheURL({
url: '/accounts/self/preferences.edit',
+ reportUrlAsIs: true,
});
}
// These defaults should match the defaults in
@@ -696,6 +775,7 @@
url: '/accounts/self/preferences',
body: prefs,
errFn: opt_errFn,
+ reportUrlAsIs: true,
});
},
@@ -711,6 +791,7 @@
url: '/accounts/self/preferences.diff',
body: prefs,
errFn: opt_errFn,
+ reportUrlAsIs: true,
});
},
@@ -726,12 +807,14 @@
url: '/accounts/self/preferences.edit',
body: prefs,
errFn: opt_errFn,
+ reportUrlAsIs: true,
});
},
getAccount() {
return this._fetchSharedCacheURL({
url: '/accounts/self/detail',
+ reportUrlAsIs: true,
errFn: resp => {
if (!resp || resp.status === 403) {
this._cache['/accounts/self/detail'] = null;
@@ -741,7 +824,10 @@
},
getExternalIds() {
- return this._fetchJSON({url: '/accounts/self/external.ids'});
+ return this._fetchJSON({
+ url: '/accounts/self/external.ids',
+ reportUrlAsIs: true,
+ });
},
deleteAccountIdentity(id) {
@@ -750,6 +836,7 @@
url: '/accounts/self/external.ids:delete',
body: id,
parseResponse: true,
+ reportUrlAsIs: true,
});
},
@@ -760,11 +847,15 @@
getAccountDetails(userId) {
return this._fetchJSON({
url: `/accounts/${encodeURIComponent(userId)}/detail`,
+ anonymizedUrl: '/accounts/*/detail',
});
},
getAccountEmails() {
- return this._fetchSharedCacheURL({url: '/accounts/self/emails'});
+ return this._fetchSharedCacheURL({
+ url: '/accounts/self/emails',
+ reportUrlAsIs: true,
+ });
},
/**
@@ -776,6 +867,7 @@
method: 'PUT',
url: '/accounts/self/emails/' + encodeURIComponent(email),
errFn: opt_errFn,
+ anonymizedUrl: '/account/self/emails/*',
});
},
@@ -788,6 +880,7 @@
method: 'DELETE',
url: '/accounts/self/emails/' + encodeURIComponent(email),
errFn: opt_errFn,
+ anonymizedUrl: '/accounts/self/email/*',
});
},
@@ -797,8 +890,13 @@
*/
setPreferredAccountEmail(email, opt_errFn) {
const encodedEmail = encodeURIComponent(email);
- const url = `/accounts/self/emails/${encodedEmail}/preferred`;
- return this._send({method: 'PUT', url, errFn: opt_errFn}).then(() => {
+ const req = {
+ method: 'PUT',
+ url: `/accounts/self/emails/${encodedEmail}/preferred`,
+ errFn: opt_errFn,
+ anonymizedUrl: '/accounts/self/emails/*/preferred',
+ };
+ return this._send(req).then(() => {
// If result of getAccountEmails is in cache, update it in the cache
// so we don't have to invalidate it.
const cachedEmails = this._cache['/accounts/self/emails'];
@@ -840,6 +938,7 @@
body: {name},
errFn: opt_errFn,
parseResponse: true,
+ reportUrlAsIs: true,
};
return this._send(req)
.then(newName => this._updateCachedAccount({name: newName}));
@@ -856,6 +955,7 @@
body: {username},
errFn: opt_errFn,
parseResponse: true,
+ reportUrlAsIs: true,
};
return this._send(req)
.then(newName => this._updateCachedAccount({username: newName}));
@@ -872,6 +972,7 @@
body: {status},
errFn: opt_errFn,
parseResponse: true,
+ reportUrlAsIs: true,
};
return this._send(req)
.then(newStatus => this._updateCachedAccount({status: newStatus}));
@@ -880,15 +981,22 @@
getAccountStatus(userId) {
return this._fetchJSON({
url: `/accounts/${encodeURIComponent(userId)}/status`,
+ anonymizedUrl: '/accounts/*/status',
});
},
getAccountGroups() {
- return this._fetchJSON({url: '/accounts/self/groups'});
+ return this._fetchJSON({
+ url: '/accounts/self/groups',
+ reportUrlAsIs: true,
+ });
},
getAccountAgreements() {
- return this._fetchJSON({url: '/accounts/self/agreements'});
+ return this._fetchJSON({
+ url: '/accounts/self/agreements',
+ reportUrlAsIs: true,
+ });
},
saveAccountAgreement(name) {
@@ -896,6 +1004,7 @@
method: 'PUT',
url: '/accounts/self/agreements',
body: name,
+ reportUrlAsIs: true,
});
},
@@ -911,6 +1020,7 @@
}
return this._fetchSharedCacheURL({
url: '/accounts/self/capabilities' + queryString,
+ anonymizedUrl: '/accounts/self/capabilities?q=*',
});
},
@@ -937,8 +1047,9 @@
return;
}
this._credentialCheck.checking = true;
+ const req = {url: '/accounts/self/detail', reportUrlAsIs: true};
// Skip the REST response cache.
- return this._fetchRawJSON({url: '/accounts/self/detail'}).then(res => {
+ return this._fetchRawJSON(req).then(res => {
if (!res) { return; }
if (res.status === 403) {
this.fire('auth-error');
@@ -958,21 +1069,24 @@
},
getDefaultPreferences() {
- return this._fetchSharedCacheURL({url: '/config/server/preferences'});
+ return this._fetchSharedCacheURL({
+ url: '/config/server/preferences',
+ reportUrlAsIs: true,
+ });
},
getPreferences() {
return this.getLoggedIn().then(loggedIn => {
if (loggedIn) {
- return this._fetchSharedCacheURL({url: '/accounts/self/preferences'})
- .then(res => {
- if (this._isNarrowScreen()) {
- res.default_diff_view = DiffViewMode.UNIFIED;
- } else {
- res.default_diff_view = res.diff_view;
- }
- return Promise.resolve(res);
- });
+ const req = {url: '/accounts/self/preferences', reportUrlAsIs: true};
+ return this._fetchSharedCacheURL(req).then(res => {
+ if (this._isNarrowScreen()) {
+ res.default_diff_view = DiffViewMode.UNIFIED;
+ } else {
+ res.default_diff_view = res.diff_view;
+ }
+ return Promise.resolve(res);
+ });
}
return Promise.resolve({
@@ -988,6 +1102,7 @@
getWatchedProjects() {
return this._fetchSharedCacheURL({
url: '/accounts/self/watched.projects',
+ reportUrlAsIs: true,
});
},
@@ -1002,6 +1117,7 @@
body: projects,
errFn: opt_errFn,
parseResponse: true,
+ reportUrlAsIs: true,
});
},
@@ -1015,6 +1131,7 @@
url: '/accounts/self/watched.projects:delete',
body: projects,
errFn: opt_errFn,
+ reportUrlAsIs: true,
});
},
@@ -1079,7 +1196,12 @@
this._maybeInsertInLookup(change);
}
};
- return this._fetchJSON({url: '/changes/', params}).then(response => {
+ const req = {
+ url: '/changes/',
+ params,
+ reportUrlAsIs: true,
+ };
+ return this._fetchJSON(req).then(response => {
// Response may be an array of changes OR an array of arrays of
// changes.
if (opt_query instanceof Array) {
@@ -1173,6 +1295,7 @@
cancelCondition: opt_cancelCondition,
params: {O: params},
fetchOptions: this._etags.getOptions(urlWithParams),
+ anonymizedUrl: '/changes/*~*/detail?O=' + params,
};
return this._fetchRawJSON(req).then(response => {
if (response && response.status === 304) {
@@ -1213,6 +1336,7 @@
changeNum,
endpoint: '/commit?links',
patchNum,
+ reportEndpointAsIs: true,
});
},
@@ -1233,6 +1357,7 @@
endpoint: '/files',
patchNum: patchRange.patchNum,
params,
+ reportEndpointAsIs: true,
});
},
@@ -1242,10 +1367,16 @@
*/
getChangeEditFiles(changeNum, patchRange) {
let endpoint = '/edit?list';
+ let anonymizedEndpoint = endpoint;
if (patchRange.basePatchNum !== 'PARENT') {
endpoint += '&base=' + encodeURIComponent(patchRange.basePatchNum + '');
+ anonymizedEndpoint += '&base=*';
}
- return this._getChangeURLAndFetch({changeNum, endpoint});
+ return this._getChangeURLAndFetch({
+ changeNum,
+ endpoint,
+ anonymizedEndpoint,
+ });
},
/**
@@ -1259,6 +1390,7 @@
changeNum,
endpoint: `/files?q=${encodeURIComponent(query)}`,
patchNum,
+ anonymizedEndpoint: '/files?q=*',
});
},
@@ -1287,7 +1419,12 @@
},
getChangeRevisionActions(changeNum, patchNum) {
- const req = {changeNum, endpoint: '/actions', patchNum};
+ const req = {
+ changeNum,
+ endpoint: '/actions',
+ patchNum,
+ reportEndpointAsIs: true,
+ };
return this._getChangeURLAndFetch(req).then(revisionActions => {
// The rebase button on change screen is always enabled.
if (revisionActions.rebase) {
@@ -1312,6 +1449,7 @@
endpoint: '/suggest_reviewers',
errFn: opt_errFn,
params,
+ reportEndpointAsIs: true,
});
},
@@ -1319,7 +1457,11 @@
* @param {number|string} changeNum
*/
getChangeIncludedIn(changeNum) {
- return this._getChangeURLAndFetch({changeNum, endpoint: '/in'});
+ return this._getChangeURLAndFetch({
+ changeNum,
+ endpoint: '/in',
+ reportEndpointAsIs: true,
+ });
},
_computeFilter(filter) {
@@ -1345,6 +1487,7 @@
return this._fetchSharedCacheURL({
url: `/groups/?n=${groupsPerPage + 1}&S=${offset}` +
this._computeFilter(filter),
+ anonymizedUrl: '/groups/?*',
});
},
@@ -1362,6 +1505,7 @@
return this._fetchSharedCacheURL({
url: `/projects/?d&n=${reposPerPage + 1}&S=${offset}` +
this._computeFilter(filter),
+ anonymizedUrl: '/projects/?*',
});
},
@@ -1372,6 +1516,7 @@
method: 'PUT',
url: `/projects/${encodeURIComponent(repo)}/HEAD`,
body: {ref},
+ anonymizedUrl: '/projects/*/HEAD',
});
},
@@ -1391,7 +1536,11 @@
const url = `/projects/${repo}/branches?n=${count}&S=${offset}${filter}`;
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- return this._fetchJSON({url, errFn: opt_errFn});
+ return this._fetchJSON({
+ url,
+ errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/branches?*',
+ });
},
/**
@@ -1411,7 +1560,11 @@
encodedFilter;
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- return this._fetchJSON({url, errFn: opt_errFn});
+ return this._fetchJSON({
+ url,
+ errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/tags',
+ });
},
/**
@@ -1426,7 +1579,11 @@
const encodedFilter = this._computeFilter(filter);
const n = pluginsPerPage + 1;
const url = `/plugins/?all&n=${n}&S=${offset}${encodedFilter}`;
- return this._fetchJSON({url, errFn: opt_errFn});
+ return this._fetchJSON({
+ url,
+ errFn: opt_errFn,
+ anonymizedUrl: '/plugins/?all',
+ });
},
getRepoAccessRights(repoName, opt_errFn) {
@@ -1435,6 +1592,7 @@
return this._fetchJSON({
url: `/projects/${encodeURIComponent(repoName)}/access`,
errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/access',
});
},
@@ -1445,6 +1603,7 @@
method: 'POST',
url: `/projects/${encodeURIComponent(repoName)}/access`,
body: repoInfo,
+ anonymizedUrl: '/projects/*/access',
});
},
@@ -1454,6 +1613,7 @@
url: `/projects/${encodeURIComponent(projectName)}/access:review`,
body: projectInfo,
parseResponse: true,
+ anonymizedUrl: '/projects/*/access:review',
});
},
@@ -1469,6 +1629,7 @@
url: '/groups/',
errFn: opt_errFn,
params,
+ reportUrlAsIs: true,
});
},
@@ -1488,6 +1649,7 @@
url: '/projects/',
errFn: opt_errFn,
params,
+ reportUrlAsIs: true,
});
},
@@ -1506,6 +1668,7 @@
url: '/accounts/',
errFn: opt_errFn,
params,
+ anonymizedUrl: '/accounts/?n=*',
});
},
@@ -1541,6 +1704,7 @@
changeNum,
endpoint: '/related',
patchNum,
+ reportEndpointAsIs: true,
});
},
@@ -1548,6 +1712,7 @@
return this._getChangeURLAndFetch({
changeNum,
endpoint: '/submitted_together',
+ reportEndpointAsIs: true,
});
},
@@ -1560,7 +1725,11 @@
O: options,
q: 'status:open is:mergeable conflicts:' + changeNum,
};
- return this._fetchJSON({url: '/changes/', params});
+ return this._fetchJSON({
+ url: '/changes/',
+ params,
+ anonymizedUrl: '/changes/conflicts:*',
+ });
},
getChangeCherryPicks(project, changeID, changeNum) {
@@ -1578,7 +1747,11 @@
O: options,
q: query,
};
- return this._fetchJSON({url: '/changes/', params});
+ return this._fetchJSON({
+ url: '/changes/',
+ params,
+ anonymizedUrl: '/changes/change:*',
+ });
},
getChangesWithSameTopic(topic) {
@@ -1592,7 +1765,11 @@
O: options,
q: 'status:open topic:' + topic,
};
- return this._fetchJSON({url: '/changes/', params});
+ return this._fetchJSON({
+ url: '/changes/',
+ params,
+ anonymizedUrl: '/changes/topic:*',
+ });
},
getReviewedFiles(changeNum, patchNum) {
@@ -1600,6 +1777,7 @@
changeNum,
endpoint: '/files?reviewed',
patchNum,
+ reportEndpointAsIs: true,
});
},
@@ -1617,6 +1795,7 @@
patchNum,
endpoint: `/files/${encodeURIComponent(path)}/reviewed`,
errFn: opt_errFn,
+ anonymizedEndpoint: '/files/*/reviewed',
});
},
@@ -1649,6 +1828,7 @@
changeNum,
endpoint: '/edit/',
params,
+ reportEndpointAsIs: true,
});
});
},
@@ -1679,6 +1859,7 @@
base_commit: opt_baseCommit,
},
parseResponse: true,
+ reportUrlAsIs: true,
});
},
@@ -1725,6 +1906,7 @@
endpoint: `/files/${encodeURIComponent(path)}/content`,
errFn: opt_errFn,
headers: {Accept: 'application/json'},
+ anonymizedEndpoint: '/files/*/content',
});
},
@@ -1739,6 +1921,7 @@
method: 'GET',
endpoint: '/edit/' + encodeURIComponent(path),
headers: {Accept: 'application/json'},
+ anonymizedEndpoint: '/edit/*',
});
},
@@ -1747,6 +1930,7 @@
changeNum,
method: 'POST',
endpoint: '/edit:rebase',
+ reportEndpointAsIs: true,
});
},
@@ -1755,6 +1939,7 @@
changeNum,
method: 'DELETE',
endpoint: '/edit',
+ reportEndpointAsIs: true,
});
},
@@ -1764,6 +1949,7 @@
method: 'POST',
endpoint: '/edit',
body: {restore_path},
+ reportEndpointAsIs: true,
});
},
@@ -1773,6 +1959,7 @@
method: 'POST',
endpoint: '/edit',
body: {old_path, new_path},
+ reportEndpointAsIs: true,
});
},
@@ -1781,6 +1968,7 @@
changeNum,
method: 'DELETE',
endpoint: '/edit/' + encodeURIComponent(path),
+ anonymizedEndpoint: '/edit/*',
});
},
@@ -1791,6 +1979,7 @@
endpoint: '/edit/' + encodeURIComponent(path),
body: contents,
contentType: 'text/plain',
+ anonymizedEndpoint: '/edit/*',
});
},
@@ -1801,6 +1990,7 @@
method: 'PUT',
endpoint: '/edit:message',
body: {message},
+ reportEndpointAsIs: true,
});
},
@@ -1809,6 +1999,7 @@
changeNum,
method: 'POST',
endpoint: '/edit:publish',
+ reportEndpointAsIs: true,
});
},
@@ -1818,13 +2009,16 @@
method: 'PUT',
endpoint: '/message',
body: {message},
+ reportEndpointAsIs: true,
});
},
saveChangeStarred(changeNum, starred) {
- const url = '/accounts/self/starred.changes/' + changeNum;
- const method = starred ? 'PUT' : 'DELETE';
- return this._send({method, url});
+ return this._send({
+ method: starred ? 'PUT' : 'DELETE',
+ url: '/accounts/self/starred.changes/' + changeNum,
+ anonymizedUrl: '/accounts/self/starred.changes/*',
+ });
},
/**
@@ -1850,7 +2044,12 @@
}
const url = req.url.startsWith('http') ?
req.url : this.getBaseUrl() + req.url;
- const xhr = this._fetch(url, options).then(response => {
+ const fetchReq = {
+ url,
+ fetchOptions: options,
+ anonymizedUrl: req.reportUrlAsIs ? url : req.anonymizedUrl,
+ };
+ const xhr = this._fetch(fetchReq).then(response => {
if (!response.ok) {
if (req.errFn) {
return req.errFn.call(undefined, response);
@@ -1928,6 +2127,7 @@
errFn: opt_errFn,
cancelCondition: opt_cancelCondition,
params,
+ anonymizedEndpoint: '/files/*/diff',
});
},
@@ -2019,6 +2219,7 @@
changeNum,
endpoint,
patchNum: opt_patchNum,
+ reportEndpointAsIs: true,
});
};
@@ -2109,8 +2310,10 @@
_sendDiffDraftRequest(method, changeNum, patchNum, draft) {
const isCreate = !draft.id && method === 'PUT';
let endpoint = '/drafts';
+ let anonymizedEndpoint = endpoint;
if (draft.id) {
endpoint += '/' + draft.id;
+ anonymizedEndpoint += '/*';
}
let body;
if (method === 'PUT') {
@@ -2121,8 +2324,16 @@
this._pendingRequests[Requests.SEND_DIFF_DRAFT] = [];
}
- const promise = this._getChangeURLAndSend(
- {changeNum, method, patchNum, endpoint, body});
+ const req = {
+ changeNum,
+ method,
+ patchNum,
+ endpoint,
+ body,
+ anonymizedEndpoint,
+ };
+
+ const promise = this._getChangeURLAndSend(req);
this._pendingRequests[Requests.SEND_DIFF_DRAFT].push(promise);
if (isCreate) {
@@ -2136,11 +2347,12 @@
return this._fetchJSON({
url: '/projects/' + encodeURIComponent(project) +
'/commits/' + encodeURIComponent(commit),
+ anonymizedUrl: '/projects/*/comments/*',
});
},
_fetchB64File(url) {
- return this._fetch(this.getBaseUrl() + url)
+ return this._fetch({url: this.getBaseUrl() + url})
.then(response => {
if (!response.ok) { return Promise.reject(response.statusText); }
const type = response.headers.get('X-FYI-Content-Type');
@@ -2241,6 +2453,7 @@
endpoint: '/topic',
body: {topic},
parseResponse: true,
+ reportUrlAsIs: true,
});
},
@@ -2256,6 +2469,7 @@
endpoint: '/hashtags',
body: hashtag,
parseResponse: true,
+ reportUrlAsIs: true,
});
},
@@ -2263,6 +2477,7 @@
return this._send({
method: 'DELETE',
url: '/accounts/self/password.http',
+ reportUrlAsIs: true,
});
},
@@ -2277,11 +2492,15 @@
url: '/accounts/self/password.http',
body: {generate: true},
parseResponse: true,
+ reportUrlAsIs: true,
});
},
getAccountSSHKeys() {
- return this._fetchSharedCacheURL({url: '/accounts/self/sshkeys'});
+ return this._fetchSharedCacheURL({
+ url: '/accounts/self/sshkeys',
+ reportUrlAsIs: true,
+ });
},
addAccountSSHKey(key) {
@@ -2290,6 +2509,7 @@
url: '/accounts/self/sshkeys',
body: key,
contentType: 'plain/text',
+ reportUrlAsIs: true,
};
return this._send(req)
.then(response => {
@@ -2308,15 +2528,24 @@
return this._send({
method: 'DELETE',
url: '/accounts/self/sshkeys/' + id,
+ anonymizedUrl: '/accounts/self/sshkeys/*',
});
},
getAccountGPGKeys() {
- return this._fetchJSON({url: '/accounts/self/gpgkeys'});
+ return this._fetchJSON({
+ url: '/accounts/self/gpgkeys',
+ reportUrlAsIs: true,
+ });
},
addAccountGPGKey(key) {
- const req = {method: 'POST', url: '/accounts/self/gpgkeys', body: key};
+ const req = {
+ method: 'POST',
+ url: '/accounts/self/gpgkeys',
+ body: key,
+ reportUrlAsIs: true,
+ };
return this._send(req)
.then(response => {
if (response.status < 200 && response.status >= 300) {
@@ -2334,6 +2563,7 @@
return this._send({
method: 'DELETE',
url: '/accounts/self/gpgkeys/' + id,
+ anonymizedUrl: '/accounts/self/gpgkeys/*',
});
},
@@ -2342,6 +2572,7 @@
changeNum,
method: 'DELETE',
endpoint: `/reviewers/${account}/votes/${encodeURIComponent(label)}`,
+ anonymizedEndpoint: '/reviewers/*/votes/*',
});
},
@@ -2351,6 +2582,7 @@
method: 'PUT', patchNum,
endpoint: '/description',
body: {description: desc},
+ reportUrlAsIs: true,
});
},
@@ -2359,6 +2591,7 @@
method: 'PUT',
url: '/config/server/email.confirm',
body: {token},
+ reportUrlAsIs: true,
};
return this._send(req).then(response => {
if (response.status === 204) {
@@ -2372,6 +2605,7 @@
return this._fetchJSON({
url: '/config/server/capabilities',
errFn: opt_errFn,
+ reportUrlAsIs: true,
});
},
@@ -2381,6 +2615,7 @@
method: 'PUT',
endpoint: '/assignee',
body: {assignee},
+ reportUrlAsIs: true,
});
},
@@ -2389,6 +2624,7 @@
changeNum,
method: 'DELETE',
endpoint: '/assignee',
+ reportUrlAsIs: true,
});
},
@@ -2408,7 +2644,13 @@
if (opt_message) {
body.message = opt_message;
}
- const req = {changeNum, method: 'POST', endpoint: '/wip', body};
+ const req = {
+ changeNum,
+ method: 'POST',
+ endpoint: '/wip',
+ body,
+ reportUrlAsIs: true,
+ };
return this._getChangeURLAndSend(req).then(response => {
if (response.status === 204) {
return 'Change marked as Work In Progress.';
@@ -2428,6 +2670,7 @@
endpoint: '/ready',
body: opt_body,
errFn: opt_errFn,
+ reportUrlAsIs: true,
});
},
@@ -2444,6 +2687,7 @@
endpoint: `/comments/${commentID}/delete`,
body: {reason},
parseResponse: true,
+ anonymizedEndpoint: '/comments/*/delete',
});
},
@@ -2459,6 +2703,7 @@
return this._fetchJSON({
url: `/changes/?q=change:${changeNum}`,
errFn: opt_errFn,
+ anonymizedUrl: '/changes/?q=change:*',
}).then(res => {
if (!res || !res.length) { return null; }
return res[0];
@@ -2509,6 +2754,11 @@
* @return {!Promise<!Object>}
*/
_getChangeURLAndSend(req) {
+ const anonymizedBaseUrl = req.patchNum ?
+ ANONYMIZED_REVISION_BASE_URL : ANONYMIZED_CHANGE_BASE_URL;
+ const anonymizedEndpoint = req.reportEndpointAsIs ?
+ req.endpoint : req.anonymizedEndpoint;
+
return this._changeBaseURL(req.changeNum, req.patchNum).then(url => {
return this._send({
method: req.method,
@@ -2518,6 +2768,8 @@
contentType: req.contentType,
headers: req.headers,
parseResponse: req.parseResponse,
+ anonymizedUrl: anonymizedEndpoint ?
+ (anonymizedBaseUrl + anonymizedEndpoint) : undefined,
});
});
},
@@ -2528,6 +2780,10 @@
* @return {!Promise<!Object>}
*/
_getChangeURLAndFetch(req) {
+ const anonymizedEndpoint = req.reportEndpointAsIs ?
+ req.endpoint : req.anonymizedEndpoint;
+ const anonymizedBaseUrl = req.patchNum ?
+ ANONYMIZED_REVISION_BASE_URL : ANONYMIZED_CHANGE_BASE_URL;
return this._changeBaseURL(req.changeNum, req.patchNum).then(url => {
return this._fetchJSON({
url: url + req.endpoint,
@@ -2535,6 +2791,8 @@
cancelCondition: req.cancelCondition,
params: req.params,
fetchOptions: req.fetchOptions,
+ anonymizedUrl: anonymizedEndpoint ?
+ (anonymizedBaseUrl + anonymizedEndpoint) : undefined,
});
});
},
@@ -2577,6 +2835,7 @@
endpoint: `/files/${encodedPath}/blame`,
patchNum,
params: opt_base ? {base: 't'} : undefined,
+ anonymizedEndpoint: '/files/*/blame',
});
},
@@ -2621,7 +2880,11 @@
getDashboard(project, dashboard, opt_errFn) {
const url = '/projects/' + encodeURIComponent(project) + '/dashboards/' +
encodeURIComponent(dashboard);
- return this._fetchSharedCacheURL({url, errFn: opt_errFn});
+ return this._fetchSharedCacheURL({
+ url,
+ errFn: opt_errFn,
+ anonymizedUrl: '/projects/*/dashboards/*',
+ });
},
getMergeable(changeNum) {
@@ -2629,6 +2892,7 @@
changeNum,
endpoint: '/revisions/current/mergeable',
parseResponse: true,
+ reportEndpointAsIs: true,
});
},
});
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 70d1465..193d306 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
@@ -711,11 +711,10 @@
sandbox.spy(element, '_send');
element.confirmEmail('foo');
assert.isTrue(element._send.calledOnce);
- assert.deepEqual(element._send.lastCall.args[0], {
- method: 'PUT',
- url: '/config/server/email.confirm',
- body: {token: 'foo'},
- });
+ assert.equal(element._send.lastCall.args[0].method, 'PUT');
+ assert.equal(element._send.lastCall.args[0].url,
+ '/config/server/email.confirm');
+ assert.deepEqual(element._send.lastCall.args[0].body, {token: 'foo'});
});
test('GrReviewerUpdatesParser.parse is used', () => {
@@ -924,11 +923,10 @@
const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
.returns(Promise.resolve());
return element.queryChangeFiles('42', 'edit', 'test/path.js').then(() => {
- assert.deepEqual(fetchStub.lastCall.args[0], {
- changeNum: '42',
- endpoint: '/files?q=test%2Fpath.js',
- patchNum: 'edit',
- });
+ assert.equal(fetchStub.lastCall.args[0].changeNum, '42');
+ assert.equal(fetchStub.lastCall.args[0].endpoint,
+ '/files?q=test%2Fpath.js');
+ assert.equal(fetchStub.lastCall.args[0].patchNum, 'edit');
});
});
@@ -1387,12 +1385,25 @@
sandbox.stub(element._auth, 'fetch').returns(Promise.resolve(response));
const startTime = 123;
sandbox.stub(Date, 'now').returns(startTime);
- return element._fetch(url, fetchOptions).then(() => {
+ const req = {url, fetchOptions};
+ return element._fetch(req).then(() => {
assert.isTrue(logStub.calledOnce);
- assert.isTrue(logStub.calledWith(
- url, fetchOptions, startTime, response.status));
+ assert.isTrue(logStub.calledWith(req, startTime, response.status));
assert.isFalse(response.text.called);
});
});
+
+ test('_logCall only reports requests with anonymized URLss', () => {
+ sandbox.stub(Date, 'now').returns(200);
+ const handler = sinon.stub();
+ element.addEventListener('rpc-log', handler);
+
+ element._logCall({url: 'url'}, 100, 200);
+ assert.isFalse(handler.called);
+
+ element._logCall({url: 'url', anonymizedUrl: 'not url'}, 100, 200);
+ flushAsynchronousOperations();
+ assert.isTrue(handler.calledOnce);
+ });
});
</script>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 6e61c8e..0310e58 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -204,6 +204,7 @@
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
'gr-path-list-behavior/gr-path-list-behavior_test.html',
'gr-tooltip-behavior/gr-tooltip-behavior_test.html',
+ 'safe-types-behavior/safe-types-behavior_test.html',
];
/* eslint-enable max-len */
for (let file of behaviors) {