Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  Add support for Elasticsearch version 7.3.*
  PrologEnvironment: Reduce "setting reductionLimit" log spam
  ElasticContainer: Upgrade to 6.8.2 image for V6_8 tests
  Fix typo: program
  Fix email token routing
  Clarify usage of 'parent' option in list files API
  Remove unused Skylark patch file
  Files: Use Gerrit API to get revision parents
  Fix broken link for rest-api-projects.html#commentlink-info
  Add support for Elasticsearch version 6.8.x
  Upgrade elasticsearch-rest-client to 7.2.1
  Add support for "Link Another Identity" in gr-identities
  Update git submodules
  CommitApi: Add method to get commit info
  Consolidate all CommitApi tests into a single class
  Files: Validate parent option to prevent internal server error
  RevisionIT: Assert that files(base) only works for patch set revisions
  Fix and expand documentation of REST API to get revision files
  RevisionIT#files: Simplify assertion
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  Remove default bug tracker from _feedbackUrl
  PG: Add shortcuts for dashboard and watched changes
  PG: Allow empty label values
  Remove token param from getCapabilities
  Add an extension point to show a small banner next to the search bar
  Fix gr-group-audit-log to use tbody

Change-Id: Idb3913a340ba3c9535ac67f5e4cc21e5f35ec39f
This commit is contained in:
David Pursehouse
2019-08-07 12:54:48 +09:00
40 changed files with 493 additions and 204 deletions

View File

@@ -4906,12 +4906,22 @@ need the FileInfo should make two requests.
The request parameter `q` changes the response to return a list The request parameter `q` changes the response to return a list
of all files (modified or unmodified) that contain that substring of all files (modified or unmodified) that contain that substring
in the path name. This is useful to implement suggestion services in the path name. This is useful to implement suggestion services
finding a file by partial name. finding a file by partial name. Clients that also need the FileInfo
should make two requests.
The integer-valued request parameter `parent` changes the response to return a For merge commits only, the integer-valued request parameter `parent`
list of the files which are different in this commit compared to the given changes the response to return a map of the files which are different
parent commit. This is useful for supporting review of merge commits. The value in this commit compared to the given parent commit. The value is the
is the 1-based index of the parent's position in the commit object. 1-based index of the parent's position in the commit object. If not
specified, the response contains a map of the files different in the
auto merge result.
The request parameter `base` changes the response to return a map of the
files which are different in this commit compared to the given revision. The
revision must correspond to a patch set in the change.
The `reviewed`, `q`, `parent`, and `base` options are mutually exclusive.
That is, only one of them may be used at a time.
.Request .Request
---- ----

View File

@@ -3058,12 +3058,12 @@ link:config-gerrit.html#commentlink[commentlink].
|Field Name | |Description |Field Name | |Description
|`match` | |A JavaScript regular expression to match |`match` | |A JavaScript regular expression to match
positions to be replaced with a hyperlink, as documented in positions to be replaced with a hyperlink, as documented in
link#config-gerrit.html#commentlink.name.match[commentlink.name.match]. link:config-gerrit.html#commentlink.name.match[commentlink.name.match].
|`link` | |The URL to direct the user to whenever the |`link` | |The URL to direct the user to whenever the
regular expression is matched, as documented in regular expression is matched, as documented in
link#config-gerrit.html#commentlink.name.link[commentlink.name.link]. link:config-gerrit.html#commentlink.name.link[commentlink.name.link].
|`enabled` |optional|Whether the commentlink is enabled, as documented |`enabled` |optional|Whether the commentlink is enabled, as documented
in link#config-gerrit.html#commentlink.name.enabled[ in link:config-gerrit.html#commentlink.name.enabled[
commentlink.name.enabled]. If not set the commentlink is enabled. commentlink.name.enabled]. If not set the commentlink is enabled.
|================================================== |==================================================

View File

@@ -1053,8 +1053,8 @@ maven_jar(
# and httpasyncclient as necessary. # and httpasyncclient as necessary.
maven_jar( maven_jar(
name = "elasticsearch-rest-client", name = "elasticsearch-rest-client",
artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.2.0", artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.3.0",
sha1 = "39cf34068b0af284eaa9b8bd86a131cb24b322d5", sha1 = "3cdc211c8efb72c202107b40dee356f4f2f0f9bd",
) )
maven_jar( maven_jar(

View File

@@ -25,9 +25,11 @@ public enum ElasticVersion {
V6_5("6.5.*"), V6_5("6.5.*"),
V6_6("6.6.*"), V6_6("6.6.*"),
V6_7("6.7.*"), V6_7("6.7.*"),
V6_8("6.8.*"),
V7_0("7.0.*"), V7_0("7.0.*"),
V7_1("7.1.*"), V7_1("7.1.*"),
V7_2("7.2.*"); V7_2("7.2.*"),
V7_3("7.3.*");
private final String version; private final String version;
private final Pattern pattern; private final Pattern pattern;

View File

@@ -17,10 +17,12 @@ package com.google.gerrit.extensions.api.projects;
import com.google.gerrit.extensions.api.changes.ChangeApi; import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput; import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo; import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException; import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
public interface CommitApi { public interface CommitApi {
CommitInfo get() throws RestApiException;
ChangeApi cherryPick(CherryPickInput input) throws RestApiException; ChangeApi cherryPick(CherryPickInput input) throws RestApiException;
@@ -28,6 +30,11 @@ public interface CommitApi {
/** A default implementation for source compatibility when adding new methods to the interface. */ /** A default implementation for source compatibility when adding new methods to the interface. */
class NotImplemented implements CommitApi { class NotImplemented implements CommitApi {
@Override
public CommitInfo get() throws RestApiException {
throw new NotImplementedException();
}
@Override @Override
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException { public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@@ -66,7 +66,7 @@ public final class GerritLauncher {
} }
/** /**
* Invokes a proram. * Invokes a program.
* *
* <p>Creates a new classloader to load and run the program class. To reuse a classloader across * <p>Creates a new classloader to load and run the program class. To reuse a classloader across
* calls (e.g. from tests), use {@link #invokeProgram(ClassLoader, String[])}. * calls (e.g. from tests), use {@link #invokeProgram(ClassLoader, String[])}.
@@ -176,7 +176,7 @@ public final class GerritLauncher {
} }
/** /**
* Invokes a proram in the provided {@code ClassLoader}. * Invokes a program in the provided {@code ClassLoader}.
* *
* @param loader classloader to load program class from. * @param loader classloader to load program class from.
* @param origArgv arguments, as would be passed to {@code gerrit.war}. The first argument is the * @param origArgv arguments, as would be passed to {@code gerrit.war}. The first argument is the

View File

@@ -21,10 +21,12 @@ import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.extensions.api.changes.CherryPickInput; import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo; import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.projects.CommitApi; import com.google.gerrit.extensions.api.projects.CommitApi;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.project.CommitResource; import com.google.gerrit.server.project.CommitResource;
import com.google.gerrit.server.restapi.change.CherryPickCommit; import com.google.gerrit.server.restapi.change.CherryPickCommit;
import com.google.gerrit.server.restapi.project.CommitIncludedIn; import com.google.gerrit.server.restapi.project.CommitIncludedIn;
import com.google.gerrit.server.restapi.project.GetCommit;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
@@ -34,6 +36,7 @@ public class CommitApiImpl implements CommitApi {
} }
private final Changes changes; private final Changes changes;
private final GetCommit getCommit;
private final CherryPickCommit cherryPickCommit; private final CherryPickCommit cherryPickCommit;
private final CommitIncludedIn includedIn; private final CommitIncludedIn includedIn;
private final CommitResource commitResource; private final CommitResource commitResource;
@@ -41,15 +44,26 @@ public class CommitApiImpl implements CommitApi {
@Inject @Inject
CommitApiImpl( CommitApiImpl(
Changes changes, Changes changes,
GetCommit getCommit,
CherryPickCommit cherryPickCommit, CherryPickCommit cherryPickCommit,
CommitIncludedIn includedIn, CommitIncludedIn includedIn,
@Assisted CommitResource commitResource) { @Assisted CommitResource commitResource) {
this.changes = changes; this.changes = changes;
this.getCommit = getCommit;
this.cherryPickCommit = cherryPickCommit; this.cherryPickCommit = cherryPickCommit;
this.includedIn = includedIn; this.includedIn = includedIn;
this.commitResource = commitResource; this.commitResource = commitResource;
} }
@Override
public CommitInfo get() throws RestApiException {
try {
return getCommit.apply(commitResource);
} catch (Exception e) {
throw asRestApiException("Cannot get commit info", e);
}
}
@Override @Override
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException { public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
try { try {

View File

@@ -19,6 +19,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hasher; import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.FileInfo; import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
@@ -27,8 +28,8 @@ import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.ETagView; import com.google.gerrit.extensions.restapi.ETagView;
import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
@@ -119,6 +120,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
private final PatchListCache patchListCache; private final PatchListCache patchListCache;
private final PatchSetUtil psUtil; private final PatchSetUtil psUtil;
private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore; private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
private final GerritApi gApi;
@Inject @Inject
ListFiles( ListFiles(
@@ -128,7 +130,8 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
GitRepositoryManager gitManager, GitRepositoryManager gitManager,
PatchListCache patchListCache, PatchListCache patchListCache,
PatchSetUtil psUtil, PatchSetUtil psUtil,
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) { PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore,
GerritApi gApi) {
this.self = self; this.self = self;
this.fileInfoJson = fileInfoJson; this.fileInfoJson = fileInfoJson;
this.revisions = revisions; this.revisions = revisions;
@@ -136,6 +139,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
this.patchListCache = patchListCache; this.patchListCache = patchListCache;
this.psUtil = psUtil; this.psUtil = psUtil;
this.accountPatchReviewStore = accountPatchReviewStore; this.accountPatchReviewStore = accountPatchReviewStore;
this.gApi = gApi;
} }
public ListFiles setReviewed(boolean r) { public ListFiles setReviewed(boolean r) {
@@ -145,9 +149,8 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
@Override @Override
public Response<?> apply(RevisionResource resource) public Response<?> apply(RevisionResource resource)
throws AuthException, BadRequestException, ResourceNotFoundException, throws RestApiException, RepositoryNotFoundException, IOException,
RepositoryNotFoundException, IOException, PatchListNotAvailableException, PatchListNotAvailableException, PermissionBackendException {
PermissionBackendException {
checkOptions(); checkOptions();
if (reviewed) { if (reviewed) {
return Response.ok(reviewed(resource)); return Response.ok(reviewed(resource));
@@ -165,7 +168,17 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
resource.getChange(), resource.getChange(),
resource.getPatchSet().getRevision(), resource.getPatchSet().getRevision(),
baseResource.getPatchSet())); baseResource.getPatchSet()));
} else if (parentNum > 0) { } else if (parentNum != 0) {
int parents =
gApi.changes()
.id(resource.getChange().getChangeId())
.revision(resource.getPatchSet().getId().get())
.commit(false)
.parents
.size();
if (parentNum < 0 || parentNum > parents) {
throw new BadRequestException(String.format("invalid parent number: %d", parentNum));
}
r = r =
Response.ok( Response.ok(
fileInfoJson.toFileInfoMap( fileInfoJson.toFileInfoMap(

View File

@@ -84,7 +84,6 @@ public class PrologEnvironment extends BufferingPrologControl {
public void setPredicate(Predicate goal) { public void setPredicate(Predicate goal) {
super.setPredicate(goal); super.setPredicate(goal);
int reductionLimit = args.reductionLimit(goal); int reductionLimit = args.reductionLimit(goal);
logger.atFine().log("setting reductionLimit %d", reductionLimit);
setReductionLimit(reductionLimit); setReductionLimit(reductionLimit);
} }
@@ -223,6 +222,9 @@ public class PrologEnvironment extends BufferingPrologControl {
private int reductionLimit(Predicate goal) { private int reductionLimit(Predicate goal) {
if (goal.getClass() == CONSULT_STREAM_2) { if (goal.getClass() == CONSULT_STREAM_2) {
logger.atFine().log(
"predicate class is CONSULT_STREAM_2: override reductionLimit with compileLimit (%d)",
compileLimit);
return compileLimit; return compileLimit;
} }
return reductionLimit; return reductionLimit;

View File

@@ -0,0 +1,151 @@
// 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.api.project;
import static com.google.common.truth.Truth.assertThat;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.TagInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.reviewdb.client.Branch;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
@NoHttpd
public class CommitIT extends AbstractDaemonTest {
@Test
public void getCommitInfo() throws Exception {
Result result = createChange();
String commitId = result.getCommit().getId().name();
CommitInfo info = gApi.projects().name(project.get()).commit(commitId).get();
assertThat(info.commit).isEqualTo(commitId);
assertThat(info.parents.stream().map(c -> c.commit).collect(toList()))
.containsExactly(result.getCommit().getParent(0).name());
assertThat(info.subject).isEqualTo(result.getCommit().getShortMessage());
assertPerson(info.author, admin);
assertPerson(info.committer, admin);
assertThat(info.webLinks).isNull();
}
@Test
public void includedInOpenChange() throws Exception {
Result result = createChange();
assertThat(getIncludedIn(result.getCommit().getId()).branches).isEmpty();
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
}
@Test
public void includedInMergedChange() throws Exception {
Result result = createChange();
gApi.changes()
.id(result.getChangeId())
.revision(result.getCommit().name())
.review(ReviewInput.approve());
gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
assertThat(getIncludedIn(result.getCommit().getId()).branches).containsExactly("master");
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
grant(project, R_TAGS + "*", Permission.CREATE_TAG);
gApi.projects().name(result.getChange().project().get()).tag("test-tag").create(new TagInput());
assertThat(getIncludedIn(result.getCommit().getId()).tags).containsExactly("test-tag");
createBranch(new Branch.NameKey(project.get(), "test-branch"));
assertThat(getIncludedIn(result.getCommit().getId()).branches)
.containsExactly("master", "test-branch");
}
@Test
public void cherryPickCommitWithoutChangeId() throws Exception {
// This test is a little superfluous, since the current cherry-pick code ignores
// the commit message of the to-be-cherry-picked change, using the one in
// CherryPickInput instead.
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
input.message = "it goes to foo branch";
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
RevCommit revCommit = createNewCommitWithoutChangeId("refs/heads/master", "a.txt", "content");
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
CommitInfo commitInfo = revInfo.commit;
assertThat(commitInfo.message)
.isEqualTo(input.message + "\n\nChange-Id: " + changeInfo.changeId + "\n");
}
@Test
public void cherryPickCommitWithChangeId() throws Exception {
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
RevCommit revCommit = createChange().getCommit();
List<String> footers = revCommit.getFooterLines("Change-Id");
assertThat(footers).hasSize(1);
String changeId = footers.get(0);
input.message = "it goes to foo branch\n\nChange-Id: " + changeId;
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
assertThat(revInfo.commit.message).isEqualTo(input.message + "\n");
}
private IncludedInInfo getIncludedIn(ObjectId id) throws Exception {
return gApi.projects().name(project.get()).commit(id.name()).includedIn();
}
private static void assertPerson(GitPerson actual, TestAccount expected) {
assertThat(actual.email).isEqualTo(expected.email());
assertThat(actual.name).isEqualTo(expected.fullName());
}
}

View File

@@ -1,66 +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.api.project;
import static com.google.common.truth.Truth.assertThat;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.TagInput;
import com.google.gerrit.reviewdb.client.Branch;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@NoHttpd
public class CommitIncludedInIT extends AbstractDaemonTest {
@Test
public void includedInOpenChange() throws Exception {
Result result = createChange();
assertThat(getIncludedIn(result.getCommit().getId()).branches).isEmpty();
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
}
@Test
public void includedInMergedChange() throws Exception {
Result result = createChange();
gApi.changes()
.id(result.getChangeId())
.revision(result.getCommit().name())
.review(ReviewInput.approve());
gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
assertThat(getIncludedIn(result.getCommit().getId()).branches).containsExactly("master");
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
grant(project, R_TAGS + "*", Permission.CREATE_TAG);
gApi.projects().name(result.getChange().project().get()).tag("test-tag").create(new TagInput());
assertThat(getIncludedIn(result.getCommit().getId()).tags).containsExactly("test-tag");
createBranch(new Branch.NameKey(project.get(), "test-branch"));
assertThat(getIncludedIn(result.getCommit().getId()).branches)
.containsExactly("master", "test-branch");
}
private IncludedInInfo getIncludedIn(ObjectId id) throws Exception {
return gApi.projects().name(project.get()).commit(id.name()).includedIn();
}
}

View File

@@ -26,6 +26,7 @@ import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LAB
import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG; import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST; import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.HEAD; import static org.eclipse.jgit.lib.Constants.HEAD;
@@ -1046,30 +1047,76 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange(); PushOneCommit.Result r = createChange();
Map<String, FileInfo> files = Map<String, FileInfo> files =
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(); gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files();
assertThat(files).hasSize(2); assertThat(files.keySet()).containsExactly(FILE_NAME, COMMIT_MSG);
assertThat(Iterables.all(files.keySet(), f -> f.matches(FILE_NAME + '|' + COMMIT_MSG)))
.isTrue();
} }
@Test @Test
public void filesOnMergeCommitChange() throws Exception { public void filesOnMergeCommitChange() throws Exception {
PushOneCommit.Result r = createMergeCommitChange("refs/for/master"); PushOneCommit.Result r = createMergeCommitChange("refs/for/master");
// list files against auto-merge // List files against auto-merge
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files().keySet()) assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files().keySet())
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "bar"); .containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "bar");
// list files against parent 1 // List files against parent 1
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(1).keySet()) assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(1).keySet())
.containsExactly(COMMIT_MSG, MERGE_LIST, "bar"); .containsExactly(COMMIT_MSG, MERGE_LIST, "bar");
// list files against parent 2 // List files against parent 2
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(2).keySet()) assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(2).keySet())
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo"); .containsExactly(COMMIT_MSG, MERGE_LIST, "foo");
} }
@Test
public void filesOnMergeCommitChangeWithInvalidParent() throws Exception {
PushOneCommit.Result r = createMergeCommitChange("refs/for/master");
BadRequestException thrown =
assertThrows(
BadRequestException.class,
() ->
gApi.changes()
.id(r.getChangeId())
.revision(r.getCommit().name())
.files(3)
.keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: 3");
thrown =
assertThrows(
BadRequestException.class,
() ->
gApi.changes()
.id(r.getChangeId())
.revision(r.getCommit().name())
.files(-1)
.keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: -1");
}
@Test
public void listFilesWithInvalidParent() throws Exception {
PushOneCommit.Result result1 = createChange();
String changeId = result1.getChangeId();
PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b");
String revId2 = result2.getCommit().name();
BadRequestException thrown =
assertThrows(
BadRequestException.class,
() -> gApi.changes().id(changeId).revision(revId2).files(2).keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: 2");
thrown =
assertThrows(
BadRequestException.class,
() -> gApi.changes().id(changeId).revision(revId2).files(-1).keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: -1");
}
@Test @Test
public void listFilesOnDifferentBases() throws Exception { public void listFilesOnDifferentBases() throws Exception {
RevCommit initialCommit = getHead(repo(), "HEAD");
PushOneCommit.Result result1 = createChange(); PushOneCommit.Result result1 = createChange();
String changeId = result1.getChangeId(); String changeId = result1.getChangeId();
PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b"); PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b");
@@ -1092,6 +1139,19 @@ public class RevisionIT extends AbstractDaemonTest {
.containsExactly(COMMIT_MSG, "b.txt", "c.txt"); .containsExactly(COMMIT_MSG, "b.txt", "c.txt");
assertThat(gApi.changes().id(changeId).revision(revId3).files(revId2).keySet()) assertThat(gApi.changes().id(changeId).revision(revId3).files(revId2).keySet())
.containsExactly(COMMIT_MSG, "c.txt"); .containsExactly(COMMIT_MSG, "c.txt");
ResourceNotFoundException thrown =
assertThrows(
ResourceNotFoundException.class,
() -> gApi.changes().id(changeId).revision(revId3).files(initialCommit.getName()));
assertThat(thrown).hasMessageThat().contains(initialCommit.getName());
String invalidRev = "deadbeef";
thrown =
assertThrows(
ResourceNotFoundException.class,
() -> gApi.changes().id(changeId).revision(revId3).files(invalidRev));
assertThat(thrown).hasMessageThat().contains(invalidRev);
} }
@Test @Test

View File

@@ -32,12 +32,12 @@ public class ElasticReindexIT extends AbstractReindexTests {
@ConfigSuite.Config @ConfigSuite.Config
public static Config elasticsearchV6() { public static Config elasticsearchV6() {
return getConfig(ElasticVersion.V6_7); return getConfig(ElasticVersion.V6_8);
} }
@ConfigSuite.Config @ConfigSuite.Config
public static Config elasticsearchV7() { public static Config elasticsearchV7() {
return getConfig(ElasticVersion.V7_2); return getConfig(ElasticVersion.V7_3);
} }
@Override @Override

View File

@@ -32,15 +32,11 @@ import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput; import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling; import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.client.ChangeStatus; import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo; import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput; import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.MergeInput; import com.google.gerrit.extensions.common.MergeInput;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -53,7 +49,6 @@ import com.google.gerrit.server.submit.ChangeAlreadyMergedException;
import com.google.gerrit.testing.FakeEmailSender.Message; import com.google.gerrit.testing.FakeEmailSender.Message;
import com.google.gerrit.testing.TestTimeUtil; import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
@@ -393,60 +388,6 @@ public class CreateChangeIT extends AbstractDaemonTest {
assertCreateSucceeds(in); assertCreateSucceeds(in);
} }
@Test
public void cherryPickCommitWithoutChangeId() throws Exception {
// This test is a little superfluous, since the current cherry-pick code ignores
// the commit message of the to-be-cherry-picked change, using the one in
// CherryPickInput instead.
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
input.message = "it goes to foo branch";
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
RevCommit revCommit = createNewCommitWithoutChangeId("refs/heads/master", "a.txt", "content");
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
CommitInfo commitInfo = revInfo.commit;
assertThat(commitInfo.message)
.isEqualTo(input.message + "\n\nChange-Id: " + changeInfo.changeId + "\n");
}
@Test
public void cherryPickCommitWithChangeId() throws Exception {
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
RevCommit revCommit = createChange().getCommit();
List<String> footers = revCommit.getFooterLines("Change-Id");
assertThat(footers).hasSize(1);
String changeId = footers.get(0);
input.message = "it goes to foo branch\n\nChange-Id: " + changeId;
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
assertThat(revInfo.commit.message).isEqualTo(input.message + "\n");
}
@Test @Test
public void createChangeOnExistingBranchNotPermitted() throws Exception { public void createChangeOnExistingBranchNotPermitted() throws Exception {
createBranch(new Branch.NameKey(project, "foo")); createBranch(new Branch.NameKey(project, "foo"));

View File

@@ -31,12 +31,12 @@ public class ElasticIndexIT extends AbstractIndexTests {
@ConfigSuite.Config @ConfigSuite.Config
public static Config elasticsearchV6() { public static Config elasticsearchV6() {
return getConfig(ElasticVersion.V6_7); return getConfig(ElasticVersion.V6_8);
} }
@ConfigSuite.Config @ConfigSuite.Config
public static Config elasticsearchV7() { public static Config elasticsearchV7() {
return getConfig(ElasticVersion.V7_2); return getConfig(ElasticVersion.V7_3);
} }
@Override @Override

View File

@@ -50,12 +50,16 @@ public class ElasticContainer extends ElasticsearchContainer {
return "blacktop/elasticsearch:6.6.2"; return "blacktop/elasticsearch:6.6.2";
case V6_7: case V6_7:
return "blacktop/elasticsearch:6.7.2"; return "blacktop/elasticsearch:6.7.2";
case V6_8:
return "blacktop/elasticsearch:6.8.2";
case V7_0: case V7_0:
return "blacktop/elasticsearch:7.0.1"; return "blacktop/elasticsearch:7.0.1";
case V7_1: case V7_1:
return "blacktop/elasticsearch:7.1.1"; return "blacktop/elasticsearch:7.1.1";
case V7_2: case V7_2:
return "blacktop/elasticsearch:7.2.0"; return "blacktop/elasticsearch:7.2.1";
case V7_3:
return "blacktop/elasticsearch:7.3.0";
} }
throw new IllegalStateException("No tests for version: " + version.name()); throw new IllegalStateException("No tests for version: " + version.name());
} }

View File

@@ -41,7 +41,7 @@ public class ElasticV6QueryAccountsTest extends AbstractQueryAccountsTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V6_7); container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
} }

View File

@@ -41,7 +41,7 @@ public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V6_7); container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
} }

View File

@@ -41,7 +41,7 @@ public class ElasticV6QueryGroupsTest extends AbstractQueryGroupsTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V6_7); container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
} }

View File

@@ -41,7 +41,7 @@ public class ElasticV6QueryProjectsTest extends AbstractQueryProjectsTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V6_7); container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
} }

View File

@@ -41,7 +41,7 @@ public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V7_2); container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
} }

View File

@@ -47,7 +47,7 @@ public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V7_2); container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
client = HttpAsyncClients.createDefault(); client = HttpAsyncClients.createDefault();
client.start(); client.start();

View File

@@ -41,7 +41,7 @@ public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V7_2); container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
} }

View File

@@ -41,7 +41,7 @@ public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest {
return; return;
} }
container = ElasticContainer.createAndStart(ElasticVersion.V7_2); container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort()); nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
} }

View File

@@ -43,6 +43,9 @@ public class ElasticVersionTest extends GerritBaseTests {
assertThat(ElasticVersion.forVersion("6.7.0")).isEqualTo(ElasticVersion.V6_7); assertThat(ElasticVersion.forVersion("6.7.0")).isEqualTo(ElasticVersion.V6_7);
assertThat(ElasticVersion.forVersion("6.7.1")).isEqualTo(ElasticVersion.V6_7); assertThat(ElasticVersion.forVersion("6.7.1")).isEqualTo(ElasticVersion.V6_7);
assertThat(ElasticVersion.forVersion("6.8.0")).isEqualTo(ElasticVersion.V6_8);
assertThat(ElasticVersion.forVersion("6.8.1")).isEqualTo(ElasticVersion.V6_8);
assertThat(ElasticVersion.forVersion("7.0.0")).isEqualTo(ElasticVersion.V7_0); assertThat(ElasticVersion.forVersion("7.0.0")).isEqualTo(ElasticVersion.V7_0);
assertThat(ElasticVersion.forVersion("7.0.1")).isEqualTo(ElasticVersion.V7_0); assertThat(ElasticVersion.forVersion("7.0.1")).isEqualTo(ElasticVersion.V7_0);
@@ -51,6 +54,9 @@ public class ElasticVersionTest extends GerritBaseTests {
assertThat(ElasticVersion.forVersion("7.2.0")).isEqualTo(ElasticVersion.V7_2); assertThat(ElasticVersion.forVersion("7.2.0")).isEqualTo(ElasticVersion.V7_2);
assertThat(ElasticVersion.forVersion("7.2.1")).isEqualTo(ElasticVersion.V7_2); assertThat(ElasticVersion.forVersion("7.2.1")).isEqualTo(ElasticVersion.V7_2);
assertThat(ElasticVersion.forVersion("7.3.0")).isEqualTo(ElasticVersion.V7_3);
assertThat(ElasticVersion.forVersion("7.3.1")).isEqualTo(ElasticVersion.V7_3);
} }
@Test @Test
@@ -70,9 +76,11 @@ public class ElasticVersionTest extends GerritBaseTests {
assertThat(ElasticVersion.V6_5.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V6_5.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue(); assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue();
assertThat(ElasticVersion.V6_8.isAtLeastMinorVersion(ElasticVersion.V6_8)).isTrue();
assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V7_2.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V7_2.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V7_3.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
} }
@Test @Test
@@ -84,9 +92,11 @@ public class ElasticVersionTest extends GerritBaseTests {
assertThat(ElasticVersion.V6_5.isV6OrLater()).isTrue(); assertThat(ElasticVersion.V6_5.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue(); assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue(); assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_8.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue(); assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV6OrLater()).isTrue(); assertThat(ElasticVersion.V7_1.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_2.isV6OrLater()).isTrue(); assertThat(ElasticVersion.V7_2.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_3.isV6OrLater()).isTrue();
} }
@Test @Test
@@ -98,8 +108,10 @@ public class ElasticVersionTest extends GerritBaseTests {
assertThat(ElasticVersion.V6_5.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V6_5.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_8.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue(); assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV7OrLater()).isTrue(); assertThat(ElasticVersion.V7_1.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_2.isV7OrLater()).isTrue(); assertThat(ElasticVersion.V7_2.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_3.isV7OrLater()).isTrue();
} }
} }

View File

@@ -121,9 +121,11 @@ shortcuts are.
const Shortcut = { const Shortcut = {
OPEN_SHORTCUT_HELP_DIALOG: 'OPEN_SHORTCUT_HELP_DIALOG', OPEN_SHORTCUT_HELP_DIALOG: 'OPEN_SHORTCUT_HELP_DIALOG',
GO_TO_USER_DASHBOARD: 'GO_TO_USER_DASHBOARD',
GO_TO_OPENED_CHANGES: 'GO_TO_OPENED_CHANGES', GO_TO_OPENED_CHANGES: 'GO_TO_OPENED_CHANGES',
GO_TO_MERGED_CHANGES: 'GO_TO_MERGED_CHANGES', GO_TO_MERGED_CHANGES: 'GO_TO_MERGED_CHANGES',
GO_TO_ABANDONED_CHANGES: 'GO_TO_ABANDONED_CHANGES', GO_TO_ABANDONED_CHANGES: 'GO_TO_ABANDONED_CHANGES',
GO_TO_WATCHED_CHANGES: 'GO_TO_WATCHED_CHANGES',
CURSOR_NEXT_CHANGE: 'CURSOR_NEXT_CHANGE', CURSOR_NEXT_CHANGE: 'CURSOR_NEXT_CHANGE',
CURSOR_PREV_CHANGE: 'CURSOR_PREV_CHANGE', CURSOR_PREV_CHANGE: 'CURSOR_PREV_CHANGE',
@@ -192,12 +194,16 @@ shortcuts are.
_describe(Shortcut.SEARCH, ShortcutSection.EVERYWHERE, 'Search'); _describe(Shortcut.SEARCH, ShortcutSection.EVERYWHERE, 'Search');
_describe(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, ShortcutSection.EVERYWHERE, _describe(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, ShortcutSection.EVERYWHERE,
'Show this dialog'); 'Show this dialog');
_describe(Shortcut.GO_TO_USER_DASHBOARD, ShortcutSection.EVERYWHERE,
'Go to User Dashboard');
_describe(Shortcut.GO_TO_OPENED_CHANGES, ShortcutSection.EVERYWHERE, _describe(Shortcut.GO_TO_OPENED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Opened Changes'); 'Go to Opened Changes');
_describe(Shortcut.GO_TO_MERGED_CHANGES, ShortcutSection.EVERYWHERE, _describe(Shortcut.GO_TO_MERGED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Merged Changes'); 'Go to Merged Changes');
_describe(Shortcut.GO_TO_ABANDONED_CHANGES, ShortcutSection.EVERYWHERE, _describe(Shortcut.GO_TO_ABANDONED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Abandoned Changes'); 'Go to Abandoned Changes');
_describe(Shortcut.GO_TO_WATCHED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Watched Changes');
_describe(Shortcut.CURSOR_NEXT_CHANGE, ShortcutSection.ACTIONS, _describe(Shortcut.CURSOR_NEXT_CHANGE, ShortcutSection.ACTIONS,
'Select next change'); 'Select next change');

View File

@@ -43,8 +43,8 @@ limitations under the License.
<tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]"> <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
<td>Loading...</td> <td>Loading...</td>
</tr> </tr>
<template is="dom-repeat" items="[[_auditLog]]" <tbody class$="[[computeLoadingClass(_loading)]]">
class$="[[computeLoadingClass(_loading)]]"> <template is="dom-repeat" items="[[_auditLog]]">
<tr class="table"> <tr class="table">
<td class="date"> <td class="date">
<gr-date-formatter <gr-date-formatter
@@ -70,6 +70,7 @@ limitations under the License.
</td> </td>
</tr> </tr>
</template> </template>
</tbody>
</table> </table>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface> <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template> </template>

View File

@@ -197,10 +197,11 @@
}); });
for (const key of keys) { for (const key of keys) {
if (!values[key]) { return; } let text = values[key];
if (!text) { text = ''; }
// The value from the server being used to choose which item is // The value from the server being used to choose which item is
// selected is in integer form, so this must be converted. // selected is in integer form, so this must be converted.
valuesArr.push({value: parseInt(key, 10), text: values[key]}); valuesArr.push({value: parseInt(key, 10), text});
} }
return valuesArr; return valuesArr;
}, },

View File

@@ -195,6 +195,9 @@ limitations under the License.
</template> </template>
</ul> </ul>
<div class="rightItems"> <div class="rightItems">
<gr-endpoint-decorator
class="hideOnMobile"
name="header-small-banner"></gr-endpoint-decorator>
<gr-smart-search <gr-smart-search
id="search" id="search"
search-query="{{searchQuery}}"></gr-smart-search> search-query="{{searchQuery}}"></gr-smart-search>

View File

@@ -349,6 +349,13 @@ limitations under the License.
return this._navigate(this.getUrlForSearchQuery(query, opt_offset)); return this._navigate(this.getUrlForSearchQuery(query, opt_offset));
}, },
/**
* Navigate to the user's dashboard
*/
navigateToUserDashboard() {
return this._navigate(this.getUrlForUserDashboard('self'));
},
/** /**
* @param {!Object} change The change object. * @param {!Object} change The change object.
* @param {number=} opt_patchNum * @param {number=} opt_patchNum

View File

@@ -1411,9 +1411,13 @@
}, },
_handleSettingsLegacyRoute(data) { _handleSettingsLegacyRoute(data) {
// email tokens may contain '+' but no space.
// The parameter parsing replaces all '+' with a space,
// undo that to have valid tokens.
const token = data.params[0].replace(/ /g, '+');
this._setParams({ this._setParams({
view: Gerrit.Nav.View.SETTINGS, view: Gerrit.Nav.View.SETTINGS,
emailToken: data.params[0], emailToken: token,
}); });
}, },

View File

@@ -670,6 +670,14 @@ limitations under the License.
}); });
}); });
test('_handleSettingsLegacyRoute with +', () => {
const data = {params: {0: 'my-token test'}};
assertDataToParams(data, '_handleSettingsLegacyRoute', {
view: Gerrit.Nav.View.SETTINGS,
emailToken: 'my-token+test',
});
});
test('_handleSettingsRoute', () => { test('_handleSettingsRoute', () => {
const data = {}; const data = {};
assertDataToParams(data, '_handleSettingsRoute', { assertDataToParams(data, '_handleSettingsRoute', {

View File

@@ -232,7 +232,8 @@ limitations under the License.
<div> <div>
<a class="feedback" <a class="feedback"
href$="[[_feedbackUrl]]" href$="[[_feedbackUrl]]"
rel="noopener" target="_blank">Send feedback</a> rel="noopener"
hidden$="[[!_showFeedbackUrl(_feedbackUrl)]]">Send feedback</a>
| Press &ldquo;?&rdquo; for keyboard shortcuts | Press &ldquo;?&rdquo; for keyboard shortcuts
<gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator> <gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator>
</div> </div>

View File

@@ -87,11 +87,7 @@
computed: '_computePluginScreenName(params)', computed: '_computePluginScreenName(params)',
}, },
_settingsUrl: String, _settingsUrl: String,
_feedbackUrl: { _feedbackUrl: String,
type: String,
value: 'https://bugs.chromium.org/p/gerrit/issues/entry' +
'?template=PolyGerrit%20Issue',
},
// Used to allow searching on mobile // Used to allow searching on mobile
mobileSearch: { mobileSearch: {
type: Boolean, type: Boolean,
@@ -458,5 +454,13 @@
_mobileSearchToggle(e) { _mobileSearchToggle(e) {
this.mobileSearch = !this.mobileSearch; this.mobileSearch = !this.mobileSearch;
}, },
_showFeedbackUrl(feedbackUrl) {
if (feedbackUrl) {
return feedbackUrl;
}
return false;
},
}); });
})(); })();

View File

@@ -16,6 +16,7 @@ limitations under the License.
--> -->
<link rel="import" href="../../../bower_components/polymer/polymer.html"> <link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html"> <link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../../styles/gr-form-styles.html"> <link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html"> <link rel="import" href="../../admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
@@ -48,9 +49,12 @@ limitations under the License.
.deleteButton:not(.show) { .deleteButton:not(.show) {
display: none; display: none;
} }
.space {
margin-bottom: 1em;
}
</style> </style>
<div class="gr-form-styles"> <div class="gr-form-styles">
<fieldset> <fieldset class="space">
<table> <table>
<thead> <thead>
<tr> <tr>
@@ -80,6 +84,13 @@ limitations under the License.
</tbody> </tbody>
</table> </table>
</fieldset> </fieldset>
<dom-if if="[[_showLinkAnotherIdentity]]">
<fieldset>
<a href$="[[_computeLinkAnotherIdentity()]]">
<gr-button id="linkAnotherIdentity" link>Link Another Identity</gr-button>
</a>
</fieldset>
</dom-if>
</div> </div>
<gr-overlay id="overlay" with-backdrop> <gr-overlay id="overlay" with-backdrop>
<gr-confirm-delete-item-dialog <gr-confirm-delete-item-dialog

View File

@@ -17,6 +17,11 @@
(function() { (function() {
'use strict'; 'use strict';
const AUTH = [
'OPENID',
'OAUTH',
];
Polymer({ Polymer({
is: 'gr-identities', is: 'gr-identities',
_legacyUndefinedCheck: true, _legacyUndefinedCheck: true,
@@ -24,7 +29,16 @@
properties: { properties: {
_identities: Object, _identities: Object,
_idName: String, _idName: String,
serverConfig: Object,
_showLinkAnotherIdentity: {
type: Boolean,
computed: '_computeShowLinkAnotherIdentity(serverConfig)',
}, },
},
behaviors: [
Gerrit.BaseUrlBehavior,
],
loadData() { loadData() {
return this.$.restAPI.getExternalIds().then(id => { return this.$.restAPI.getExternalIds().then(id => {
@@ -64,5 +78,24 @@
filterIdentities(item) { filterIdentities(item) {
return !item.identity.startsWith('username:'); return !item.identity.startsWith('username:');
}, },
_computeShowLinkAnotherIdentity(config) {
if (config && config.auth &&
config.auth.git_basic_auth_policy) {
return AUTH.includes(
config.auth.git_basic_auth_policy.toUpperCase());
}
return false;
},
_computeLinkAnotherIdentity() {
const baseUrl = this.getBaseUrl() || '';
let pathname = window.location.pathname;
if (baseUrl) {
pathname = '/' + pathname.substring(baseUrl.length);
}
return baseUrl + '/login/' + encodeURIComponent(pathname) + '?link';
},
}); });
})(); })();

View File

@@ -122,5 +122,65 @@ limitations under the License.
MockInteractions.tap(deleteBtn); MockInteractions.tap(deleteBtn);
assert.isTrue(deleteItem.called); assert.isTrue(deleteItem.called);
}); });
test('_computeShowLinkAnotherIdentity', () => {
let serverConfig;
serverConfig = {
auth: {
git_basic_auth_policy: 'OAUTH',
},
};
assert.isTrue(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'OpenID',
},
};
assert.isTrue(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'HTTP_LDAP',
},
};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'LDAP',
},
};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'HTTP',
},
};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
});
test('_showLinkAnotherIdentity', () => {
element.serverConfig = {
auth: {
git_basic_auth_policy: 'OAUTH',
},
};
assert.isTrue(element._showLinkAnotherIdentity);
element.serverConfig = {
auth: {
git_basic_auth_policy: 'LDAP',
},
};
assert.isFalse(element._showLinkAnotherIdentity);
});
}); });
</script> </script>

View File

@@ -417,7 +417,7 @@ limitations under the License.
</fieldset> </fieldset>
<h2 id="Identities">Identities</h2> <h2 id="Identities">Identities</h2>
<fieldset> <fieldset>
<gr-identities id="identities"></gr-identities> <gr-identities id="identities" server-config="[[_serverConfig]]"></gr-identities>
</fieldset> </fieldset>
<template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]"> <template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
<h2 id="Agreements">Agreements</h2> <h2 id="Agreements">Agreements</h2>

View File

@@ -2763,7 +2763,7 @@
}); });
}, },
getCapabilities(token, opt_errFn) { getCapabilities(opt_errFn) {
return this._fetchJSON({ return this._fetchJSON({
url: '/config/server/capabilities', url: '/config/server/capabilities',
errFn: opt_errFn, errFn: opt_errFn,