blob: cf3df545fdee33115e8f26564e677406f3dd5302 [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2010, 2013 Sony Ericsson/ST Ericsson and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sony Ericsson/ST Ericsson - initial API and implementation
* Tasktop Technologies - improvements
* Sascha Scholz (SAP) - improvements
* GitHub, Inc. - fixes for bug 354753
* Christian Trutz - improvements
* Francois Chouinard - Added "LABELS" option on selected queries
* Jacques Bouthillier - Bug 414253 Add support for Gerrit Dashboard
* Jacques Bouthillier (Ericsson) - Bug 426505 Add Starred functionality
* Guy Perron (Ericsson) Bug 423242 Add ability to edit comment from compare navigator popup
* Guy Perron/Jacques Bouthillier Bug 437825 support Gerrit 2.9 with API changes
*********************************************************************/
package org.eclipse.mylyn.internal.gerrit.core.client;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.net.AbstractWebLocation;
import org.eclipse.mylyn.commons.net.WebUtil;
import org.eclipse.mylyn.internal.gerrit.core.GerritCorePlugin;
import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritHttpClient.ErrorHandler;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritHttpClient.Request;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritService.GerritRequest;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ChangeDetailService;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ChangeDetailX;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ChangeManageService;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.GerritConfigX;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.GerritSystemAccount;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.PatchDetailService;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.PatchScriptX;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.PatchSetPublishDetailX;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ProjectAdminService;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.ProjectDetailX;
import org.eclipse.mylyn.internal.gerrit.core.client.compat.SubmitRecord;
import org.eclipse.mylyn.internal.gerrit.core.client.data.GerritQueryResult;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.AbandonInput;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ActionInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.AddReviewerResult;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ChangeInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ChangeInfo29;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ChangeMessageInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.CommentInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.CommentInput;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ProjectInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.RelatedChangeAndCommitInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.RelatedChangesInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.RestoreInput;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ReviewInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ReviewInput;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ReviewerInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.ReviewerInput;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.RevisionInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.SubmitInfo;
import org.eclipse.mylyn.internal.gerrit.core.client.rest.SubmitInput;
import org.eclipse.mylyn.internal.gerrit.core.remote.GerritRemoteFactoryProvider;
import org.eclipse.mylyn.reviews.core.model.IRepository;
import org.eclipse.mylyn.reviews.core.model.IReview;
import org.eclipse.mylyn.reviews.core.spi.ReviewsClient;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.AbstractRemoteEmfFactoryProvider;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Version;
import com.google.gerrit.common.data.AccountDashboardInfo;
import com.google.gerrit.common.data.AccountInfo;
import com.google.gerrit.common.data.AccountInfoCache;
import com.google.gerrit.common.data.AccountService;
import com.google.gerrit.common.data.ApprovalDetail;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.ChangeListService;
import com.google.gerrit.common.data.GerritConfig;
import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.common.data.ReviewerResult;
import com.google.gerrit.common.data.SingleListChangeInfo;
import com.google.gerrit.common.data.ToggleStarRequest;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountDiffPreference;
import com.google.gerrit.reviewdb.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.ChangeMessage;
import com.google.gerrit.reviewdb.ContributorAgreement;
import com.google.gerrit.reviewdb.Patch;
import com.google.gerrit.reviewdb.Patch.ChangeType;
import com.google.gerrit.reviewdb.PatchLineComment;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.PatchSet.Id;
import com.google.gerrit.reviewdb.PatchSetInfo;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.Project.NameKey;
import com.google.gerrit.reviewdb.UserIdentity;
import com.google.gson.reflect.TypeToken;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.client.VoidResult;
/**
* Facade to the Gerrit RPC API.
*
* @author Mikael Kober
* @author Thomas Westling
* @author Steffen Pingel
* @author Christian Trutz
* @author Sascha Scholz
* @author Miles Parker
* @author Francois Chouinard
* @author Jacques Bouthillier
* @author Guy Perron
*/
public class GerritClient extends ReviewsClient {
private static final Pattern GERRIT_VERSION_PATTERN = Pattern.compile("Powered by Gerrit Code Review (.+)</p>"); //$NON-NLS-1$
private static final String GET_LABELS_OPTION = "LABELS"; //$NON-NLS-1$
private final String NOT_SIGNED_IN = "Not Signed In"; //$NON-NLS-1$
private final String OK = "OK"; //$NON-NLS-1$
private final String NEED = "NEED"; //$NON-NLS-1$
private final String REJECT = "REJECT"; //$NON-NLS-1$
final String MAY = "MAY"; //$NON-NLS-1$
private abstract class Operation<T> implements AsyncCallback<T> {
private Throwable exception;
private T result;
public abstract void execute(IProgressMonitor monitor) throws GerritException;
public Throwable getException() {
return exception;
}
public T getResult() {
return result;
}
public void onFailure(Throwable exception) {
if (isAuthenticationException(exception)) {
// invalidate login cookie to force re-authentication
client.setXsrfCookie(null);
}
this.exception = exception;
}
public void onSuccess(T result) {
setResult(result);
}
protected void setResult(T result) {
this.result = result;
}
public void reset() {
this.result = null;
this.exception = null;
}
}
public boolean isAuthenticationException(Throwable exception) {
if (exception instanceof GerritException) {
return ((GerritException) exception).getCode() == -32603
&& "Invalid xsrfKey in request".equals(((GerritException) exception).getMessage()); //$NON-NLS-1$
}
return false;
}
public boolean isNotSignedInException(Throwable exception) {
if (exception instanceof GerritException) {
return ((GerritException) exception).getCode() == -32603
&& NOT_SIGNED_IN.equalsIgnoreCase(((GerritException) exception).getMessage());
}
return false;
}
// XXX belongs in GerritConnector
public static GerritAuthenticationState authStateFromString(String token) {
try {
JSonSupport support = new JSonSupport();
return support.parseResponse(token, GerritAuthenticationState.class);
} catch (Exception e) {
// ignore
return null;
}
}
// XXX belongs in GerritConnector
public static String authStateToString(GerritAuthenticationState authState) {
if (authState == null) {
return null;
}
try {
JSonSupport support = new JSonSupport();
return support.toJson(authState);
} catch (Exception e) {
// ignore
return null;
}
}
private final GerritHttpClient client;
private volatile GerritConfiguration config;
private Account myAcount;
private Version myVersion;
private final Map<Class<? extends RemoteJsonService>, RemoteJsonService> serviceByClass;
private volatile boolean configRefreshed;
/**
* The GWT query API was removed in Gerrit 2.5 and replaced with a REST API. If this flag is true, the REST API is
* used.
*/
private boolean restQueryAPIEnabled;
public GerritClient(TaskRepository repository, AbstractWebLocation location) {
this(repository, location, null, null, null);
}
public GerritClient(TaskRepository repository, AbstractWebLocation location, GerritConfiguration config,
GerritAuthenticationState authState) {
this(repository, location, config, authState, null);
}
public GerritClient(TaskRepository repository, AbstractWebLocation location, GerritConfiguration config,
GerritAuthenticationState authState, String xsrfKey) {
super(repository);
this.client = new GerritHttpClient(location) {
@Override
protected void sessionChanged(Cookie cookie) {
GerritAuthenticationState authState = new GerritAuthenticationState();
authState.setCookie(cookie);
authStateChanged(authState);
}
};
if (authState != null) {
client.setXsrfCookie(authState.getCookie());
}
if (xsrfKey != null) {
client.setXsrfKey(xsrfKey);
}
this.serviceByClass = new HashMap<Class<? extends RemoteJsonService>, RemoteJsonService>();
this.config = config;
}
public PatchLineComment saveDraft(Patch.Key patchKey, String message, int line, short side, String parentUuid,
String uuid, IProgressMonitor monitor) throws GerritException {
PatchLineComment.Key id = new PatchLineComment.Key(patchKey, uuid);
final PatchLineComment comment = new PatchLineComment(id, line, getAccount(monitor).getId(), parentUuid);
comment.setMessage(message);
comment.setSide(side);
if (isVersion29OrLater(monitor)) {
if (uuid == null) {
uuid = ""; //$NON-NLS-1$
}
CommentInput commentInput = new CommentInput();
commentInput.setLine(line);
commentInput.setMessage(message);
commentInput.setPath(patchKey.getFileName());
if (side == 0) {
commentInput.setSide("PARENT"); //$NON-NLS-1$
} else {
commentInput.setSide("REVISION"); //$NON-NLS-1$
}
String uri = "/changes/" + Integer.toString(patchKey.getParentKey().getParentKey().get()) //$NON-NLS-1$
+ "/revisions/" + patchKey.getParentKey().get() + "/drafts"; //$NON-NLS-1$ //$NON-NLS-2$
if (!uuid.isEmpty()) {
uri = uri.concat("/" + uuid); //$NON-NLS-1$
}
executePutRestRequest(uri, commentInput, CommentInput.class, null/*no error handler*/, monitor);
return comment;
} else {
return execute(monitor, new Operation<PatchLineComment>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getPatchDetailService(monitor).saveDraft(comment, this);
}
});
}
}
public VoidResult deleteDraft(Patch.Key patchkey, String uuid, IProgressMonitor monitor) throws GerritException {
final PatchLineComment.Key id = new PatchLineComment.Key(patchkey, uuid);
if (isVersion29OrLater(monitor)) {
CommentInput commentInput = new CommentInput();
String uri = "/changes/" + Integer.toString(patchkey.getParentKey().getParentKey().get()) //$NON-NLS-1$
+ "/revisions/" + patchkey.getParentKey().get() + "/drafts/" + uuid; //$NON-NLS-1$ //$NON-NLS-2$
executeDeleteRestRequest(uri, commentInput, CommentInput.class, null/*no error handler*/, monitor);
return null;
} else {
return execute(monitor, new Operation<VoidResult>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getPatchDetailService(monitor).deleteDraft(id, this);
}
});
}
}
public ChangeDetail abandon(String reviewId, int patchSetId, final String message, IProgressMonitor monitor)
throws GerritException {
final PatchSet.Id id = new PatchSet.Id(new Change.Id(id(reviewId)), patchSetId);
if (hasJsonRpcApi(monitor)) {
return execute(monitor, new Operation<ChangeDetail>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeManageService(monitor).abandonChange(id, message, this);
}
});
} else {
final String uri = "/a/changes/" + id.getParentKey().get() + "/abandon"; //$NON-NLS-1$ //$NON-NLS-2$
executePostRestRequest(uri, new AbandonInput(message), ChangeInfo.class, null/*no error handler*/, monitor);
return getChangeDetail(id.getParentKey().get(), monitor);
}
}
public Object getChangeInfo29(String query, Class returnClass, IProgressMonitor monitor) throws GerritException {
return executeGetRestRequest(query, returnClass, monitor);
}
private List<PatchSet> getPatchSets29(final String changeInfoId, int reviewId, IProgressMonitor monitor)
throws GerritException {
List<PatchSet> patchSets = new ArrayList<PatchSet>();
String query = "/changes/?q=" + changeInfoId + "+change:" + reviewId + "&o=ALL_REVISIONS"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
ChangeInfo29[] changeInfo = (ChangeInfo29[]) getChangeInfo29(query, ChangeInfo29[].class, monitor);
for (ChangeInfo element : changeInfo) {
for (Entry<String, RevisionInfo> revisionInfo : element.getRevisions().entrySet()) {
PatchSet.Id patchSetId = new PatchSet.Id(new Change.Id(reviewId), revisionInfo.getValue().getNumber());
patchSets.add(new PatchSet(patchSetId));
}
}
Collections.sort(patchSets, new Comparator<PatchSet>() {
@Override
public int compare(PatchSet p1, PatchSet p2) {
return p1.getPatchSetId() - p2.getPatchSetId();
}
});
return patchSets;
}
public RelatedChangesInfo getRelatedChanges(final int reviewId, String revisionId, IProgressMonitor monitor)
throws GerritException {
String query = "/changes/" + Integer.toString(reviewId) + "/revisions/" + revisionId + "/related"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
RelatedChangesInfo relatedChangesInfo = (RelatedChangesInfo) getChangeInfo29(query, RelatedChangesInfo.class,
monitor);
return relatedChangesInfo;
}
private ChangeMessage convertChangeMessage(int reviewId, ChangeInfo changeInfo, ChangeMessageInfo changeMessageInfo) {
Change.Id changeId = new Change.Id(reviewId);
ChangeMessage.Key changeMessageKey = new ChangeMessage.Key(changeId, changeInfo.getId());
org.eclipse.mylyn.internal.gerrit.core.client.rest.AccountInfo author = changeMessageInfo.getAuthor();
if (author == null) {
//if Author is not set, the ChangeMessageInfo was created by the Gerrit system
author = GerritSystemAccount.GERRIT_SYSTEM;
}
Account.Id accountId = new Account.Id(author.getId());
ChangeMessage changeMessage = new ChangeMessage(changeMessageKey, accountId, changeMessageInfo.getDate());
changeMessage.setMessage(changeMessageInfo.getMesssage());
return changeMessage;
}
private Branch.NameKey getBranchKey(ChangeInfo changeInfo) {
Project.NameKey projectKey = new Project.NameKey(changeInfo.getProject());
Branch.NameKey branchKey = new Branch.NameKey(projectKey, changeInfo.getBranch());
return branchKey;
}
private Change createChange(String keyString, int changeIdValue, AccountInfo accountInfo, Branch.NameKey branchKey) {
Change.Key key = new Change.Key(keyString);
Change.Id changeId = new Change.Id(changeIdValue);
Change change = new Change(key, changeId, accountInfo.getId(), branchKey);
return change;
}
private PatchSetInfo getPatchSetInfo(PatchSet.Id patchsetId, String subject) {
PatchSetInfo patchSetInfo = new PatchSetInfo(patchsetId);
patchSetInfo.setSubject(subject);
return patchSetInfo;
}
private com.google.gerrit.common.data.ChangeInfo convertToGoogleChangeInfo(RelatedChangeAndCommitInfo info,
AccountInfo accountInfo, Branch.NameKey branchKey) {
Change change = createChange(info.getChangeId(), info.getChangeNumber(), accountInfo, branchKey);
PatchSet.Id patchsetId = new PatchSet.Id(change.getId(), info.getCurrentRevisionNumbe());
PatchSetInfo patchSetInfo = getPatchSetInfo(patchsetId, info.getCommitInfo().getSubject());
change.setCurrentPatchSet(patchSetInfo);
com.google.gerrit.common.data.ChangeInfo googleChangeInfo = new com.google.gerrit.common.data.ChangeInfo(change);
return googleChangeInfo;
}
public ChangeDetailX getChangeDetail29(int reviewId, IProgressMonitor monitor) throws GerritException {
ChangeDetailX changeDetail = null;
String query = "/changes/" + Integer.toString(reviewId) + "/detail/?o=ALL_REVISIONS&o=MESSAGES"; //$NON-NLS-1$//$NON-NLS-2$
ChangeInfo29 changeInfo = (ChangeInfo29) getChangeInfo29(query, ChangeInfo29.class, monitor);
List<PatchSet> patchSets = getPatchSets29(changeInfo.getChangeId(), reviewId, monitor);
List<ChangeMessage> listChangeMessage = new ArrayList<ChangeMessage>();
List<ChangeMessageInfo> listChangeMessageInfo = changeInfo.getMessages();
boolean containsMessageFromGerritSystem = false;
for (ChangeMessageInfo changeMessageInfo : listChangeMessageInfo) {
if (changeMessageInfo.getAuthor() == null) {
//if Author is not set, the ChangeMessageInfo was created by the Gerrit system
containsMessageFromGerritSystem = true;
}
ChangeMessage changeMessage = convertChangeMessage(reviewId, changeInfo, changeMessageInfo);
listChangeMessage.add(changeMessage);
}
List<AccountInfo> listAccountInfo = new ArrayList<AccountInfo>();
AccountInfo accountInfo = convertAuthorFrom29ToAccountInfo(changeInfo);
listAccountInfo.add(accountInfo);
if (containsMessageFromGerritSystem) {
//add Gerrit system if there was a ChangeMessageInfo that was created by the Gerrit system
listAccountInfo.add(org.eclipse.mylyn.internal.gerrit.core.client.compat.GerritSystemAccount.GERRIT_SYSTEM.getGerritSystemAccountInfo());
}
AccountInfoCache accountInfoCache = new AccountInfoCache(listAccountInfo);
changeDetail = new ChangeDetailX();
changeDetail.setDateCreated(changeInfo.getCreated());
changeDetail.setLastModified(changeInfo.getUpdated());
changeDetail.setStarred(changeInfo.getStarred() != null ? true : false);
changeDetail.setAccounts(accountInfoCache);
changeDetail.setMessages(listChangeMessage);
Branch.NameKey branchKey = getBranchKey(changeInfo);
setChangeDetailDependency(reviewId, changeInfo, changeDetail, accountInfo, monitor);
PatchSetInfo patchSetInfo = getPatchSetInfo(changeInfo.getCurrentPatchSetId(), changeInfo.getSubject());
changeDetail.setApprovals(changeInfo.convertToApprovalDetails());
changeDetail.setApprovalTypes(changeInfo.convertToApprovalTypes());
//Fill the submit records
String querysubmit = "/changes/" + Integer.toString(reviewId) + "/revisions/current/test.submit_rule?filters=SKIP"; //$NON-NLS-1$//$NON-NLS-2$
List<SubmitRecord> submitRecord = currentSubmitRecord29(querysubmit, monitor);
changeDetail.setSubmitRecords(submitRecord);
if (changeDetail.getApprovalTypes() == null && getGerritConfig() != null) {
changeDetail.convertSubmitRecordsToApprovalTypes(getGerritConfig().getApprovalTypes());
}
changeDetail.setPatchSets(patchSets);
List<ReviewerInfo> reviewers = listReviewers(reviewId, monitor);
if (!hasAllReviewers(changeDetail.getAccounts(), reviewers)) {
merge(changeDetail.getAccounts(), reviewers);
}
Change initialChange = createChange(changeInfo.getChangeId(), reviewId, accountInfo, branchKey);
initialChange.setCurrentPatchSet(patchSetInfo);
initialChange.setStatus(changeInfo.getStatus());
changeDetail.setChange(initialChange);
setActions29(reviewId, changeDetail, monitor);
return changeDetail;
}
private List<SubmitRecord> currentSubmitRecord29(String uri, IProgressMonitor monitor) throws GerritException {
List<SubmitRecord> submitRecordList = new ArrayList<SubmitRecord>();
SubmitRecord[] submitRecordArray = executePostRestRequest(uri, new SubmitRecord(), SubmitRecord[].class,
new ErrorHandler() {
@Override
public void handleError(HttpMethodBase method) throws GerritException {
String errorMsg = getResponseBodyAsString(method);
if (isNotPermitted(method, errorMsg) || isConflict(method)) {
throw new GerritException(NLS.bind("Cannot get submit change: {0}", errorMsg)); //$NON-NLS-1$
}
}
private String getResponseBodyAsString(HttpMethodBase method) {
try {
return method.getResponseBodyAsString();
} catch (IOException e) {
return null;
}
}
private boolean isNotPermitted(HttpMethodBase method, String msg) {
return method.getStatusCode() == HttpURLConnection.HTTP_FORBIDDEN
&& "submit not permitted\n".equals(msg); //$NON-NLS-1$
}
private boolean isConflict(HttpMethodBase method) {
return method.getStatusCode() == HttpURLConnection.HTTP_CONFLICT;
}
}, monitor);
for (SubmitRecord element : submitRecordArray) {
List<SubmitRecord.Label> list = null;
if (element.getStatus().equalsIgnoreCase("OK")) { //$NON-NLS-1$
list = element.createLabel(element, element.getOkMap(), OK);
} else if (element.getStatus().equalsIgnoreCase("NOT_READY")) { //$NON-NLS-1$
list = element.createLabel(element, element.getNeedMap(), NEED);
} else if (element.getStatus().equalsIgnoreCase("REJECT")) { //$NON-NLS-1$
list = element.createLabel(element, element.getRejectMap(), REJECT);
} else if (element.getStatus().equalsIgnoreCase("MAY")) { //$NON-NLS-1$
list = element.createLabel(element, element.getMayMap(), MAY);
}
element.setLabels(list);
submitRecordList.add(element);
}
return submitRecordList;
}
private void setChangeDetailDependency(int reviewId, ChangeInfo29 changeInfo29, ChangeDetailX changeDetail,
AccountInfo accountInfo, IProgressMonitor monitor) throws GerritException {
List<com.google.gerrit.common.data.ChangeInfo> dependsOn = new ArrayList<com.google.gerrit.common.data.ChangeInfo>();
List<com.google.gerrit.common.data.ChangeInfo> neededBy = new ArrayList<com.google.gerrit.common.data.ChangeInfo>();
Branch.NameKey branchKey = getBranchKey(changeInfo29);
RelatedChangesInfo relatedChangesInfo = getRelatedChanges(reviewId, changeInfo29.getCurrentRevision(), monitor);
List<RelatedChangeAndCommitInfo> listCommitInfo = relatedChangesInfo.getCommitInfo();
boolean needed = true;
for (RelatedChangeAndCommitInfo relatedChangeAndCommitInfo : listCommitInfo) {
if (relatedChangeAndCommitInfo.getCommitInfo()
.getCommit()
.equalsIgnoreCase(changeInfo29.getCurrentRevision())) {
needed = false;
} else {
if (relatedChangeAndCommitInfo.getChangeNumber() > 0) {
com.google.gerrit.common.data.ChangeInfo googleChangeInfo = convertToGoogleChangeInfo(
relatedChangeAndCommitInfo, accountInfo, branchKey);
if (needed) {
neededBy.add(googleChangeInfo);
} else {
dependsOn.add(googleChangeInfo);
}
}
}
}
changeDetail.setNeededBy(neededBy);
changeDetail.setDependsOn(dependsOn);
}
/**
* Returns the details for a specific review.
*/
public ChangeDetailX getChangeDetail(int reviewId, IProgressMonitor monitor) throws GerritException {
final Change.Id changeId = new Change.Id(reviewId);
ChangeDetailX changeDetail = null;
if (isVersion29OrLater(monitor)) {
changeDetail = getChangeDetail29(reviewId, monitor);
} else {
changeDetail = execute(monitor, new Operation<ChangeDetailX>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeDetailService(monitor).changeDetailX(changeId, this);
}
});
if (isVersion26OrLater(monitor)) {
if (changeDetail.getApprovals() == null) {
ChangeInfo changeInfo = getChangeInfo(reviewId, monitor);
changeDetail.setApprovals(changeInfo.convertToApprovalDetails());
changeDetail.setApprovalTypes(changeInfo.convertToApprovalTypes());
}
List<ReviewerInfo> reviewers = listReviewers(reviewId, monitor);
if (!hasAllReviewers(changeDetail.getAccounts(), reviewers)) {
merge(changeDetail.getAccounts(), reviewers);
}
} else if (changeDetail.getApprovalTypes() == null && getGerritConfig() != null) {
changeDetail.convertSubmitRecordsToApprovalTypes(getGerritConfig().getApprovalTypes());
}
changeDetail.setDateCreated(changeDetail.getChange().getCreatedOn());
changeDetail.setLastModified(changeDetail.getChange().getLastUpdatedOn());
}
return changeDetail;
}
private AccountInfo convertAuthorFrom29ToAccountInfo(ChangeInfo changeInfo) {
AccountInfo accountInfo;
Account account;
account = new Account(new Account.Id(changeInfo.getOwner().getId()));
account.setFullName(changeInfo.getOwner().getName());
account.setUserName(changeInfo.getOwner().getUsername());
account.setPreferredEmail(changeInfo.getOwner().getEmail());
accountInfo = new AccountInfo(account);
return accountInfo;
}
private org.eclipse.mylyn.internal.gerrit.core.client.rest.AccountInfo getAccountInfo(String account,
IProgressMonitor monitor) throws GerritException, URIException {
if (GerritSystemAccount.GERRIT_SYSTEM_NAME.equals(account)) {
return GerritSystemAccount.GERRIT_SYSTEM;
}
String st = URIUtil.encodeQuery(account);
final String uri = "/accounts/" + st; //$NON-NLS-1$
org.eclipse.mylyn.internal.gerrit.core.client.rest.AccountInfo accountInfo = executeGetRestRequest(uri,
org.eclipse.mylyn.internal.gerrit.core.client.rest.AccountInfo.class, monitor);
return accountInfo;
}
private void setActions29(int reviewId, ChangeDetailX changeDetail, IProgressMonitor monitor) {
ChangeInfo29 changeInfo29 = null;
try {
changeInfo29 = executeGetRestRequest("/changes/" + Integer.toString(reviewId) //$NON-NLS-1$
+ "/?o=CURRENT_REVISION&o=CURRENT_ACTIONS", ChangeInfo29.class, monitor); //$NON-NLS-1$
} catch (GerritException e) {
StatusHandler.log(new Status(IStatus.ERROR, GerritCorePlugin.PLUGIN_ID,
"ChangeDetailX GerritException running rest query", e)); //$NON-NLS-1$
}
if (changeInfo29 != null) {
if (changeInfo29.getRevisions() != null) {
setRevisionActions29(changeInfo29, changeDetail);
}
if (changeInfo29.getActions() != null) {
setGlobalActions29(changeInfo29, changeDetail);
}
}
}
private void setRevisionActions29(ChangeInfo29 changeInfo29, ChangeDetailX changeDetail) {
for (Entry<String, RevisionInfo> mapRevisions : changeInfo29.getRevisions().entrySet()) {
if (mapRevisions.getValue().getActions() != null) {
for (Entry<String, ActionInfo> mapActions : mapRevisions.getValue().getActions().entrySet()) {
if (mapActions.getKey().equalsIgnoreCase("submit")) { //$NON-NLS-1$
changeDetail.setCanSubmit(mapActions.getValue().getEnabled());
} else if (mapActions.getKey().equalsIgnoreCase("rebase")) { //$NON-NLS-1$
changeDetail.setCanRebase(mapActions.getValue().getEnabled());
}
}
}
}
}
private void setGlobalActions29(ChangeInfo29 changeInfo29, ChangeDetailX changeDetail) {
for (Entry<String, ActionInfo> mapActions : changeInfo29.getActions().entrySet()) {
if (mapActions.getKey().equalsIgnoreCase("abandon")) { //$NON-NLS-1$
changeDetail.setCanAbandon(mapActions.getValue().getEnabled());
changeDetail.setCanRestore(!mapActions.getValue().getEnabled());
} else if (mapActions.getKey().equalsIgnoreCase("restore")) { //$NON-NLS-1$
changeDetail.setCanAbandon(!mapActions.getValue().getEnabled());
changeDetail.setCanRestore(mapActions.getValue().getEnabled());
}
}
}
private List<ReviewerInfo> listReviewers(final int reviewId, IProgressMonitor monitor) throws GerritException {
final String uri = "/changes/" + reviewId + "/reviewers/"; //$NON-NLS-1$ //$NON-NLS-2$
TypeToken<List<ReviewerInfo>> reviewersListType = new TypeToken<List<ReviewerInfo>>() {
};
return executeGetRestRequest(uri, reviewersListType.getType(), monitor);
}
private boolean hasAllReviewers(AccountInfoCache accounts, List<ReviewerInfo> reviewers) {
for (ReviewerInfo reviewer : reviewers) {
AccountInfo cachedAccount = accounts.get(new Account.Id(reviewer.getId()));
if (cachedAccount == null || isAnonymous(cachedAccount)) {
return false;
}
}
return true;
}
private boolean isAnonymous(AccountInfo accountInfo) {
return accountInfo.getFullName() == null && accountInfo.getPreferredEmail() == null;
}
private void merge(AccountInfoCache accounts, List<ReviewerInfo> reviewers) {
Set<com.google.gerrit.common.data.AccountInfo> accountInfos = new HashSet<com.google.gerrit.common.data.AccountInfo>(
reviewers.size());
for (ReviewerInfo reviewer : reviewers) {
accountInfos.add(reviewer.toAccountInfo());
}
AccountInfoCache accountInfoCache = new AccountInfoCache(accountInfos);
accounts.merge(accountInfoCache);
}
public ChangeInfo getChangeInfo(final int reviewId, IProgressMonitor monitor) throws GerritException {
final String uri = "/changes/" + reviewId + "/revisions/current/review"; //$NON-NLS-1$ //$NON-NLS-2$
return executeGetRestRequest(uri, ChangeInfo.class, monitor);
}
public void loadPatchSetContent(PatchSetContent patchSetContent, IProgressMonitor monitor) throws GerritException {
Id baseId = (patchSetContent.getBase() != null) ? patchSetContent.getBase().getId() : null;
Id targetId = patchSetContent.getTarget().getId();
if (patchSetContent.getTargetDetail() == null) {
PatchSetDetail targetDetail = getPatchSetDetail(baseId, targetId, monitor);
patchSetContent.setTargetDetail(targetDetail);
}
for (Patch patch : patchSetContent.getTargetDetail().getPatches()) {
PatchScriptX patchScript = getPatchScript(patch.getKey(), baseId, targetId, monitor);
if (patchScript != null) {
patchSetContent.putPatchScriptByPatchKey(patch.getKey(), patchScript);
}
}
}
public GerritConfigX getGerritConfig() {
return config == null ? null : config.getGerritConfig();
}
public GerritConfiguration getConfiguration() {
return config;
}
public GerritSystemInfo getInfo(IProgressMonitor monitor) throws GerritException {
Version version = getCachedVersion(monitor);
List<ContributorAgreement> contributorAgreements = null;
Account account = null;
if (!isAnonymous()) {
account = getAccount(monitor);
} else {
// XXX should run some more meaningful validation as anonymous, for now any call is good to validate the URL etc.
executeQuery(monitor, "status:open"); //$NON-NLS-1$
}
refreshConfigOnce(monitor);
return new GerritSystemInfo(version, contributorAgreements, account);
}
PatchScriptX getPatchScript(final Patch.Key key, final PatchSet.Id leftId, final PatchSet.Id rightId,
final IProgressMonitor monitor) throws GerritException {
final AccountDiffPreference diffPrefs = createAccountDiffPreference();
final PatchScriptX patchScript = execute(monitor, new Operation<PatchScriptX>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getPatchDetailService(monitor).patchScriptX(key, leftId, rightId, diffPrefs, this);
}
});
if (patchScript.isBinary()) {
fetchLeftBinaryContent(patchScript, key, leftId, monitor);
fetchRightBinaryContent(patchScript, key, rightId, monitor);
}
return patchScript;
}
protected void fetchLeftBinaryContent(final PatchScriptX patchScript, final Patch.Key key,
final PatchSet.Id leftId, final IProgressMonitor monitor) throws GerritException {
if (patchScript.getChangeType() != ChangeType.ADDED) {
byte[] binaryContent = fetchBinaryContent(getUrlForPatchSetOrBase(key, leftId), monitor);
patchScript.setBinaryA(binaryContent);
}
}
protected void fetchRightBinaryContent(final PatchScriptX patchScript, final Patch.Key key,
final PatchSet.Id rightId, final IProgressMonitor monitor) throws GerritException {
if (patchScript.getChangeType() != ChangeType.DELETED) {
byte[] binaryContent = fetchBinaryContent(getUrlForPatchSet(key, rightId), monitor);
patchScript.setBinaryB(binaryContent);
}
}
protected String getUrlForPatchSetOrBase(final Patch.Key key, final PatchSet.Id id) throws GerritException {
if (id == null) {
return getUrlForBase(key);
} else {
return getUrlForPatchSet(key, id);
}
}
private String getUrlForBase(final Patch.Key key) throws GerritException {
return encode(key.toString() + "^1"); //$NON-NLS-1$
}
protected String getUrlForPatchSet(final Patch.Key key, final PatchSet.Id id) throws GerritException {
return encode(id + "," + key.getFileName() + "^0"); //$NON-NLS-1$//$NON-NLS-2$
}
protected byte[] fetchBinaryContent(String url, IProgressMonitor monitor) throws GerritException {
final TypeToken<Byte[]> byteArrayType = new TypeToken<Byte[]>() {
};
byte[] bin = executeGetRestRequest("/cat/" + url, byteArrayType.getType(), monitor); //$NON-NLS-1$
if (isZippedContent(bin)) {
return unzip(bin);
} else {
return bin;
}
}
/**
* Checks for the 4 byte header that identifies a ZIP file
*
* @noreference This method is not intended to be referenced by clients.
*/
public static boolean isZippedContent(byte[] bin) {
return bin != null && bin.length > 4 && bin[0] == 'P' && bin[1] == 'K' && bin[2] == 3 && bin[3] == 4;
}
/**
* @noreference This method is not intended to be referenced by clients.
*/
public static byte[] unzip(byte[] zip) throws GerritException {
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zip));
try {
zis.getNextEntry(); // expecting a single entry
return IOUtils.toByteArray(zis);
} catch (IOException e) {
throw new GerritException(e);
} finally {
IOUtils.closeQuietly(zis);
}
}
private AccountDiffPreference createAccountDiffPreference() {
AccountDiffPreference diffPrefs = new AccountDiffPreference((Account.Id) null);
diffPrefs.setLineLength(Integer.MAX_VALUE);
diffPrefs.setTabSize(4);
diffPrefs.setContext(AccountDiffPreference.WHOLE_FILE_CONTEXT);
diffPrefs.setIgnoreWhitespace(Whitespace.IGNORE_NONE);
diffPrefs.setIntralineDifference(false);
return diffPrefs;
}
private PatchSetDetail getPatchSetDetail(final PatchSet.Id idBase, final PatchSet.Id idTarget,
IProgressMonitor monitor) throws GerritException {
PatchSetDetail patchSetDetail = null;
try {
// Gerrit 2.4+
patchSetDetail = execute(monitor, new Operation<PatchSetDetail>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeDetailService(monitor).patchSetDetail2(idBase, idTarget, createAccountDiffPreference(),
this);
}
});
} catch (GerritException e) {
try {
// fallback for Gerrit < 2.1.7
if (isNoSuchServiceError(e)) {
patchSetDetail = execute(monitor, new Operation<PatchSetDetail>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeDetailService(monitor).patchSetDetail(idTarget, this);
}
});
} else {
throw e;
}
} catch (GerritException e2) {
// fallback for Gerrit 2.1.7
String message = e2.getMessage();
if (message != null && message.contains("Error parsing request")) { //$NON-NLS-1$
patchSetDetail = execute(monitor, new Operation<PatchSetDetail>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeDetailService(monitor).patchSetDetail(idBase, idTarget,
createAccountDiffPreference(), this);
}
});
} else {
throw e2;
}
}
}
return patchSetDetail;
}
boolean isNoSuchServiceError(GerritException e) {
String message = e.getMessage();
return message != null && message.contains("No such service method"); //$NON-NLS-1$
}
public PatchSetPublishDetailX getPatchSetPublishDetail29(final PatchSet.Id id, IProgressMonitor monitor)
throws GerritException {
PatchSetPublishDetailX publishDetail = null;
publishDetail = new PatchSetPublishDetailX();
ChangeInfo changeInfo = getChangeInfo(id.getParentKey().get(), monitor);
List<AccountInfo> listAccountInfo = new ArrayList<AccountInfo>();
AccountInfo accountInfo = convertAuthorFrom29ToAccountInfo(changeInfo);
listAccountInfo.add(accountInfo);
AccountInfoCache accountInfoCache = new AccountInfoCache(listAccountInfo);
publishDetail.setAccounts(accountInfoCache);
Branch.NameKey branchKey = getBranchKey(changeInfo);
Change currentChange = createChange(changeInfo.getChangeId(), id.getParentKey().get(), accountInfo, branchKey);
currentChange.setStatus(changeInfo.getStatus());
publishDetail.setChange(currentChange);
PatchSetInfo patchSetInfo = getPatchSetInfo(changeInfo.getCurrentPatchSetId(), changeInfo.getSubject());
publishDetail.setPatchSetInfo(patchSetInfo);
publishDetail.setLabels(changeInfo.convertToPermissionLabels());
if (publishDetail.getGiven() == null) {
publishDetail.setGiven(changeInfo.convertToPatchSetApprovals(id, getAccount(monitor)));
}
return publishDetail;
}
public PatchSetPublishDetailX getPatchSetPublishDetail(final PatchSet.Id id, IProgressMonitor monitor)
throws GerritException {
PatchSetPublishDetailX publishDetail = null;
if (isVersion29OrLater(monitor)) {
publishDetail = getPatchSetPublishDetail29(id, monitor);
} else {
publishDetail = execute(monitor, new Operation<PatchSetPublishDetailX>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeDetailService(monitor).patchSetPublishDetailX(id, this);
}
});
if (publishDetail.getLabels() == null && isVersion26OrLater(monitor)) {
ChangeInfo changeInfo = getChangeInfo(id.getParentKey().get(), monitor);
publishDetail.setLabels(changeInfo.convertToPermissionLabels());
if (publishDetail.getGiven() == null) {
publishDetail.setGiven(changeInfo.convertToPatchSetApprovals(id, getAccount(monitor)));
}
}
}
return publishDetail;
}
public GerritChange getChange(String reviewId, IProgressMonitor monitor) throws GerritException {
GerritChange gerritChange = new GerritChange();
int id;
try {
id = id(reviewId);
} catch (GerritException e) {
List<GerritQueryResult> result = executeQuery(monitor, reviewId);
if (result.size() == 1) {
id = result.get(0).getNumber();
} else {
throw e;
}
}
ChangeDetailX changeDetail = getChangeDetail(id, monitor);
List<PatchSetDetail> patchSets = new ArrayList<PatchSetDetail>(changeDetail.getPatchSets().size());
Map<PatchSet.Id, PatchSetPublishDetailX> patchSetPublishDetailByPatchSetId = new HashMap<PatchSet.Id, PatchSetPublishDetailX>();
for (PatchSet patchSet : changeDetail.getPatchSets()) {
try {
PatchSetDetail patchSetDetail = getPatchSetDetail(null, patchSet.getId(), monitor);
patchSets.add(patchSetDetail);
if (!isAnonymous()) {
PatchSetPublishDetailX patchSetPublishDetail = getPatchSetPublishDetail(patchSet.getId(), monitor);
if (isVersion29OrLater(monitor)) {
//To add the AccountId if available
patchSetPublishDetail.setPatchSetInfo(setAccountPatchSetInfo(patchSetDetail.getInfo(), monitor));
}
patchSetPublishDetailByPatchSetId.put(patchSet.getId(), patchSetPublishDetail);
changeDetail.setCurrentPatchSetDetail(patchSetDetail);
}
} catch (GerritException e) {
handleMissingPatchSet(
NLS.bind("Patch Set {0} items for Review {1}", patchSet.getPatchSetId(), reviewId), e); //$NON-NLS-1$
}
}
gerritChange.setChangeDetail(changeDetail);
gerritChange.setPatchSets(patchSets);
gerritChange.setPatchSetPublishDetailByPatchSetId(patchSetPublishDetailByPatchSetId);
return gerritChange;
}
private PatchSetInfo setAccountPatchSetInfo(PatchSetInfo patchSetInfo, IProgressMonitor monitor) {
if (patchSetInfo.getAuthor().getAccount() == null) {
patchSetInfo.setAuthor(setUserIdentity(patchSetInfo.getAuthor().getName(), patchSetInfo.getAuthor(),
"Author", monitor)); //$NON-NLS-1$
}
if (patchSetInfo.getCommitter().getAccount() == null) {
patchSetInfo.setCommitter(setUserIdentity(patchSetInfo.getCommitter().getName(),
patchSetInfo.getCommitter(), "Committer", monitor)); //$NON-NLS-1$
}
return patchSetInfo;
}
private UserIdentity setUserIdentity(String name, UserIdentity userIdentity, String user, IProgressMonitor monitor) {
org.eclipse.mylyn.internal.gerrit.core.client.rest.AccountInfo accountInfo = null;
try {
accountInfo = getAccountInfo(name, monitor);
Account.Id accountId = new Account.Id(accountInfo.getId());
userIdentity.setAccount(accountId);
} catch (GerritException gerritException) {
if (gerritException.getMessage().indexOf(HttpStatus.SC_NOT_FOUND) != 0) {
StatusHandler.log(new Status(IStatus.WARNING, GerritCorePlugin.PLUGIN_ID, NLS.bind(
"GerritException {0} not found", user), gerritException)); //$NON-NLS-1$
}
} catch (URIException uriException) {
StatusHandler.log(new Status(IStatus.ERROR, GerritCorePlugin.PLUGIN_ID,
NLS.bind("{0} URIException: ", user), uriException)); //$NON-NLS-1$
}
return userIdentity;
}
private void handleMissingPatchSet(String desc, GerritException e) {
GerritCorePlugin.logWarning(
NLS.bind("Couldn't load {0}. (Perhaps the Patch Set has been removed from repository?)", desc), e); //$NON-NLS-1$
}
public int id(String id) throws GerritException {
if (id == null) {
throw new GerritException("Invalid ID (null)"); //$NON-NLS-1$
}
try {
return Integer.parseInt(id);
} catch (NumberFormatException e) {
throw new GerritException(NLS.bind("Invalid ID (''{0}'')", id)); //$NON-NLS-1$
}
}
public void publishComments(String reviewId, int patchSetId, final String message,
final Set<ApprovalCategoryValue.Id> approvals, IProgressMonitor monitor) throws GerritException {
final PatchSet.Id id = new PatchSet.Id(new Change.Id(id(reviewId)), patchSetId);
if (hasJsonRpcApi(monitor)) {
execute(monitor, new Operation<VoidResult>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getPatchDetailService(monitor).publishComments(id, message, approvals, this);
}
});
} else {
ReviewInput reviewInput = new ReviewInput(message);
Map<String, CommentInfo[]> drafts = listDrafts(id, monitor);
Map<String, CommentInput[]> comments = convert(drafts);
if (!comments.isEmpty()) {
reviewInput.setComments(comments);
}
reviewInput.setApprovals(approvals);
final String uri = "/a/changes/" + id.getParentKey().get() + "/revisions/" + id.get() + "/review"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
executePostRestRequest(uri, reviewInput, ReviewInfo.class, new ErrorHandler() {
@Override
public void handleError(HttpMethodBase method) throws GerritException {
if (method.getStatusCode() == HttpURLConnection.HTTP_FORBIDDEN) {
String msg = getResponseBodyAsString(method);
if (msg.startsWith("Applying label") && msg.endsWith("is restricted")) { //$NON-NLS-1$ //$NON-NLS-2$
throw new GerritException(msg);
}
}
}
private String getResponseBodyAsString(HttpMethodBase method) {
try {
String msg = method.getResponseBodyAsString();
return msg.trim();
} catch (IOException e) {
// ignore
}
return null;
}
}, monitor);
}
}
private Map<String, CommentInfo[]> listDrafts(final PatchSet.Id id, IProgressMonitor monitor)
throws GerritException {
String uri = "/changes/" + id.getParentKey().get() + "/revisions/" + id.get() + "/drafts/"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
TypeToken<Map<String, CommentInfo[]>> resultType = new TypeToken<Map<String, CommentInfo[]>>() {
};
return executeGetRestRequest(uri, resultType.getType(), monitor);
}
private Map<String, CommentInput[]> convert(Map<String, CommentInfo[]> commentInfos) {
if (commentInfos == null || commentInfos.isEmpty()) {
return Collections.<String, CommentInput[]> emptyMap();
}
Map<String, CommentInput[]> commentInputs = new HashMap<String, CommentInput[]>(commentInfos.size());
Set<Entry<String, CommentInfo[]>> entrySet = commentInfos.entrySet();
for (Entry<String, CommentInfo[]> entry : entrySet) {
CommentInfo[] infos = entry.getValue();
List<CommentInput> inputs = new ArrayList<CommentInput>(infos.length);
for (CommentInfo info : infos) {
inputs.add(new CommentInput(info));
}
commentInputs.put(entry.getKey(), inputs.toArray(new CommentInput[inputs.size()]));
}
return commentInputs;
}
public ReviewerResult addReviewers(String reviewId, final List<String> reviewers, IProgressMonitor monitor)
throws GerritException {
Assert.isLegal(reviewers != null, "reviewers cannot be null"); //$NON-NLS-1$
final Change.Id id = new Change.Id(id(reviewId));
if (hasJsonRpcApi(monitor)) {
try {
return execute(monitor, new Operation<ReviewerResult>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getPatchDetailService(monitor).addReviewers(id, reviewers, this);
}
});
} catch (GerritException e) {
// Gerrit 2.2
String message = e.getMessage();
if (message != null && message.contains("Error parsing request")) { //$NON-NLS-1$
return execute(monitor, new Operation<ReviewerResult>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getPatchDetailService(monitor).addReviewers(id, reviewers, false, this);
}
});
} else {
throw e;
}
}
} else {
final String uri;
uri = "/a/changes/" + id.get() + "/reviewers"; //$NON-NLS-1$ //$NON-NLS-2$
Set<ReviewerInfo> reviewerInfos = new HashSet<ReviewerInfo>(reviewers.size());
ReviewerResult reviewerResult = new ReviewerResult();
for (final String reviewerId : reviewers) {
try {
AddReviewerResult addReviewerResult = executePostRestRequest(uri, new ReviewerInput(reviewerId),
AddReviewerResult.class, null /*no error handler*/, monitor);
reviewerInfos.addAll(addReviewerResult.getReviewers());
} catch (GerritHttpException e) {
if (e.getResponseCode() == HttpStatus.SC_UNPROCESSABLE_ENTITY) {
reviewerResult.addError(new ReviewerResult.Error(null /* no type*/, reviewerId));
}
}
}
ChangeDetail changeDetail = getChangeDetail(id.get(), monitor);
List<ApprovalDetail> approvalDetails = new ArrayList<ApprovalDetail>(reviewerInfos.size());
for (ReviewerInfo reviewerInfo : reviewerInfos) {
approvalDetails.add(reviewerInfo.toApprovalDetail(changeDetail.getCurrentPatchSet()));
}
changeDetail.setApprovals(approvalDetails);
reviewerResult.setChange(changeDetail);
return reviewerResult;
}
}
/**
* Returns the latest 25 reviews.
*/
public List<GerritQueryResult> queryAllReviews(IProgressMonitor monitor) throws GerritException {
return executeQuery(monitor, "status:open"); //$NON-NLS-1$
}
/**
* Returns the latest 25 reviews for the given project.
*/
public List<GerritQueryResult> queryByProject(IProgressMonitor monitor, final String project)
throws GerritException {
return executeQuery(monitor, "status:open project:" + project); //$NON-NLS-1$
}
/**
* Returns changes associated with the logged in user. This includes all open, closed and review requests for the
* user. On Gerrit 2.4 and earlier closed reviews are not included.
*/
public List<GerritQueryResult> queryMyReviews(IProgressMonitor monitor) throws GerritException {
if (hasJsonRpcApi(monitor) && !restQueryAPIEnabled) {
try {
final Account account = getAccount(monitor);
AccountDashboardInfo ad = execute(monitor, new Operation<AccountDashboardInfo>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeListService(monitor).forAccount(account.getId(), this);
}
});
List<com.google.gerrit.common.data.ChangeInfo> allMyChanges = ad.getByOwner();
allMyChanges.addAll(ad.getForReview());
allMyChanges.addAll(ad.getClosed());
return convert(allMyChanges);
} catch (GerritException e) {
if (isNoSuchServiceError(e)) {
restQueryAPIEnabled = true;
} else {
throw e;
}
}
}
// the "self" alias is only supported in Gerrit 2.5 and later
return executeQueryRest(monitor, "owner:self OR reviewer:self", GET_LABELS_OPTION); //$NON-NLS-1$
}
private boolean hasJsonRpcApi(IProgressMonitor monitor) throws GerritException {
return !isVersion26OrLater(monitor);
}
private boolean isVersion26OrLater(IProgressMonitor monitor) throws GerritException {
Version version = getCachedVersion(monitor);
return GerritVersion.isVersion26OrLater(version);
}
private boolean isVersion27OrLater(IProgressMonitor monitor) throws GerritException {
Version version = getCachedVersion(monitor);
return GerritVersion.isVersion27OrLater(version);
}
private boolean isVersion28OrLater(IProgressMonitor monitor) throws GerritException {
Version version = getCachedVersion(monitor);
return GerritVersion.isVersion28OrLater(version);
}
public boolean isVersion29OrLater(IProgressMonitor monitor) throws GerritException {
Version version = getCachedVersion(monitor);
return GerritVersion.isVersion29OrLater(version);
}
/**
* Returns watched changes of the currently logged in user
*/
public List<GerritQueryResult> queryWatchedReviews(IProgressMonitor monitor) throws GerritException {
return executeQuery(monitor, "is:watched status:open"); //$NON-NLS-1$
}
/**
* Retrieves the root URL for the Gerrit instance and attempts to parse the configuration from the JavaScript
* portion of the page.
*/
private GerritConfigX refreshGerritConfig(final IProgressMonitor monitor) throws GerritException {
try {
GerritConfigX gerritConfig = client.execute(new Request<GerritConfigX>() {
@Override
public HttpMethodBase createMethod() throws IOException {
return new GetMethod(client.getUrl() + "/"); //$NON-NLS-1$
}
@Override
public GerritConfigX process(HttpMethodBase method) throws IOException {
InputStream in = WebUtil.getResponseBodyAsStream(method, monitor);
try {
GerritHtmlProcessor processor = new GerritHtmlProcessor();
processor.parse(in, method.getResponseCharSet());
return processor.getConfig();
} finally {
in.close();
}
}
}, monitor);
if (gerritConfig == null) {
throw new GerritException("Failed to obtain Gerrit configuration"); //$NON-NLS-1$
}
return gerritConfig;
} catch (UnknownHostException cause) {
GerritException e = new GerritException("Unknown host: " + cause.getMessage()); //$NON-NLS-1$
e.initCause(cause);
throw e;
} catch (IOException cause) {
GerritException e = new GerritException(cause.getMessage());
e.initCause(cause);
throw e;
}
}
public GerritConfiguration refreshConfig(IProgressMonitor monitor) throws GerritException {
configRefreshed = true;
GerritConfigX gerritConfig = refreshGerritConfig(monitor);
List<Project> projects = getVisibleProjects(monitor, gerritConfig);
Account account = null;
try {
account = getAccount(monitor);
} catch (GerritException e) {
if (!isNotSignedInException(e)) {
throw e;
}
}
config = new GerritConfiguration(gerritConfig, projects, account);
configurationChanged(config);
return config;
}
public GerritConfiguration refreshConfigOnce(IProgressMonitor monitor) throws GerritException {
if (!configRefreshed && config == null) {
try {
refreshConfig(monitor);
} catch (GerritException e) {
// don't fail validation in case config parsing fails
}
}
return getConfiguration();
}
public ChangeDetail rebase(String reviewId, int patchSetId, IProgressMonitor monitor) throws GerritException {
final PatchSet.Id id = new PatchSet.Id(new Change.Id(id(reviewId)), patchSetId);
if (isVersion29OrLater(monitor)) {
return rebaseRest(id, monitor);
} else {
return execute(monitor, new Operation<ChangeDetail>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeManageService(monitor).rebaseChange(id, this);
}
});
}
}
private ChangeDetail rebaseRest(final PatchSet.Id id, IProgressMonitor monitor) throws GerritException {
final String uri = "/a/changes/" + id.getParentKey().get() + "/revisions/" + id.get() + "/rebase"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
executePostRestRequest(uri, new ChangeInfo29(), ChangeInfo29.class, new ErrorHandler() {
@Override
public void handleError(HttpMethodBase method) throws GerritException {
String errorMsg = getResponseBodyAsString(method);
if (isConflict(method)) {
throw new GerritException(errorMsg);
}
}
private String getResponseBodyAsString(HttpMethodBase method) {
try {
return method.getResponseBodyAsString();
} catch (IOException e) {
return null;
}
}
private boolean isConflict(HttpMethodBase method) {
return method.getStatusCode() == HttpURLConnection.HTTP_CONFLICT;
}
}, monitor);
return getChangeDetail(id.getParentKey().get(), monitor);
}
public ChangeDetail restore(String reviewId, int patchSetId, final String message, IProgressMonitor monitor)
throws GerritException {
final PatchSet.Id id = new PatchSet.Id(new Change.Id(id(reviewId)), patchSetId);
if (hasJsonRpcApi(monitor)) {
return execute(monitor, new Operation<ChangeDetail>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeManageService(monitor).restoreChange(id, message, this);
}
});
} else {
final String uri = "/a/changes/" + id.getParentKey().get() + "/restore"; //$NON-NLS-1$ //$NON-NLS-2$
try {
executePostRestRequest(uri, new RestoreInput(message), ChangeInfo.class, null/*no error handler*/,
monitor);
} catch (GerritHttpException e) {
if (e.getResponseCode() == HttpURLConnection.HTTP_CONFLICT) {
throw new GerritException("Not Found", e); //$NON-NLS-1$
}
}
return getChangeDetail(id.getParentKey().get(), monitor);
}
}
public ChangeDetail submit(String reviewId, int patchSetId, IProgressMonitor monitor) throws GerritException {
final PatchSet.Id id = new PatchSet.Id(new Change.Id(id(reviewId)), patchSetId);
if (hasJsonRpcApi(monitor)) {
return execute(monitor, new Operation<ChangeDetail>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeManageService(monitor).submit(id, this);
}
});
} else {
return submitRest(id, monitor);
}
}
private ChangeDetail submitRest(PatchSet.Id id, IProgressMonitor monitor) throws GerritException {
final String uri = "/a/changes/" + id.getParentKey().get() + "/revisions/" + id.get() + "/submit"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
executePostRestRequest(uri, new SubmitInput(true), SubmitInfo.class, new ErrorHandler() {
@Override
public void handleError(HttpMethodBase method) throws GerritException {
String errorMsg = getResponseBodyAsString(method);
if (isNotPermitted(method, errorMsg) || isConflict(method)) {
throw new GerritException(NLS.bind("Cannot submit change: {0}", errorMsg)); //$NON-NLS-1$
}
}
private String getResponseBodyAsString(HttpMethodBase method) {
try {
return method.getResponseBodyAsString();
} catch (IOException e) {
return null;
}
}
private boolean isNotPermitted(HttpMethodBase method, String msg) {
return method.getStatusCode() == HttpURLConnection.HTTP_FORBIDDEN
&& "submit not permitted\n".equals(msg); //$NON-NLS-1$
}
private boolean isConflict(HttpMethodBase method) {
return method.getStatusCode() == HttpURLConnection.HTTP_CONFLICT;
}
}, monitor);
return getChangeDetail(id.getParentKey().get(), monitor);
}
/**
* Sends a query for the changes visible to the caller to the gerrit server.
*
* @param monitor
* A progress monitor
* @param queryString
* The specific gerrit change query
* @return a list of GerritQueryResults built from the parsed query result (ChangeInfo:s)
* @throws GerritException
*/
public List<GerritQueryResult> executeQuery(IProgressMonitor monitor, final String queryString)
throws GerritException {
return executeQuery(monitor, queryString, GET_LABELS_OPTION);
}
/**
* Sends a query for the changes visible to the caller to the gerrit server with the possibility of adding options
* to the query.
*
* @param monitor
* A progress monitor
* @param queryString
* The specific gerrit change query
* @param optionString
* Query options ("&o=" parameter). Only applicable for the REST API, ignored otherwise. May be null.
* @return a list of GerritQueryResults built from the parsed query result (ChangeInfo:s)
* @throws GerritException
*/
public List<GerritQueryResult> executeQuery(IProgressMonitor monitor, final String queryString, String optionString)
throws GerritException {
if (hasJsonRpcApi(monitor) && !restQueryAPIEnabled) {
try {
SingleListChangeInfo sl = execute(monitor, new Operation<SingleListChangeInfo>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeListService(monitor).allQueryNext(queryString, "z", -1, this); //$NON-NLS-1$
}
});
return convert(sl.getChanges());
} catch (GerritException e) {
if (isNoSuchServiceError(e)) {
restQueryAPIEnabled = true;
} else {
throw e;
}
}
}
return executeQueryRest(monitor, queryString, optionString);
}
private List<GerritQueryResult> convert(List<com.google.gerrit.common.data.ChangeInfo> changes) {
List<GerritQueryResult> results = new ArrayList<GerritQueryResult>(changes.size());
for (com.google.gerrit.common.data.ChangeInfo changeInfo : changes) {
GerritQueryResult result = new GerritQueryResult(changeInfo);
results.add(result);
}
return results;
}
/**
* Sends a query for the changes visible to the caller to the gerrit server. Uses the gerrit REST API.
*
* @param monitor
* A progress monitor
* @param queryString
* The specific gerrit change query
* @return a list of GerritQueryResults built from the parsed query result (ChangeInfo:s)
* @throws GerritException
*/
public List<GerritQueryResult> executeQueryRest(IProgressMonitor monitor, final String queryString)
throws GerritException {
return executeQueryRest(monitor, queryString, null);
}
/**
* Sends a query for the changes visible to the caller to the gerrit server with the possibility of adding options
* to the query. Uses the gerrit REST API.
*
* @param monitor
* A progress monitor
* @param queryString
* The specific gerrit change query
* @param optionString
* Query options ("&o=" parameter). May be null or empty.
* @return a list of GerritQueryResults built from the parsed query result (ChangeInfo:s)
* @throws GerritException
*/
public List<GerritQueryResult> executeQueryRest(IProgressMonitor monitor, final String queryString,
String optionString) throws GerritException {
String uri = "/changes/?q=" + encode(queryString); //$NON-NLS-1$
if (StringUtils.isNotBlank(optionString)) {
uri += "&o=" + encode(optionString); //$NON-NLS-1$
}
TypeToken<List<GerritQueryResult>> queryResultListType = new TypeToken<List<GerritQueryResult>>() {
};
return executeGetRestRequest(uri, queryResultListType.getType(), monitor);
}
/**
* Returns the (possibly cached) account for this client.
*/
public Account getAccount(IProgressMonitor monitor) throws GerritException {
synchronized (this) {
if (myAcount != null) {
return myAcount;
}
}
Account account = executeAccount(monitor);
synchronized (this) {
myAcount = account;
}
return myAcount;
}
private Account executeAccount(IProgressMonitor monitor) throws GerritException {
if (isVersion29OrLater(monitor)) {
return getAccount29(monitor);
} else {
return execute(monitor, new Operation<Account>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getAccountService(monitor).myAccount(this);
}
});
}
}
private Account getAccount29(IProgressMonitor monitor) throws GerritException {
if (isAnonymous()) {
throw new GerritException(NOT_SIGNED_IN, -32603);
}
String query = "/accounts/self"; //$NON-NLS-1$/
org.eclipse.mylyn.internal.gerrit.core.client.rest.AccountInfo accountInfo = executeGetRestRequest(query,
org.eclipse.mylyn.internal.gerrit.core.client.rest.AccountInfo.class, monitor);
Account account = new Account(new Account.Id(accountInfo.getId()));
account.setFullName(accountInfo.getName());
account.setUserName(accountInfo.getUsername());
account.setPreferredEmail(accountInfo.getEmail());
return account;
}
private AccountService getAccountService(IProgressMonitor monitor) {
return getService(AccountService.class, monitor);
}
private ChangeDetailService getChangeDetailService(IProgressMonitor monitor) {
return getService(ChangeDetailService.class, monitor);
}
private ChangeListService getChangeListService(IProgressMonitor monitor) {
return getService(ChangeListService.class, monitor);
}
private ChangeManageService getChangeManageService(IProgressMonitor monitor) {
return getService(ChangeManageService.class, monitor);
}
private PatchDetailService getPatchDetailService(IProgressMonitor monitor) {
return getService(PatchDetailService.class, monitor);
}
private List<Project> getVisibleProjects(IProgressMonitor monitor, GerritConfig gerritConfig)
throws GerritException {
List<Project> result = new ArrayList<Project>();
try {
List<ProjectDetailX> projectDetails = execute(monitor, new Operation<List<ProjectDetailX>>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getProjectAdminService(monitor).visibleProjectDetails(this);
}
});
for (ProjectDetailX projectDetail : projectDetails) {
if (!GerritUtil.isPermissionOnlyProject(projectDetail, gerritConfig)) {
result.add(projectDetail.project);
}
}
} catch (GerritException e) {
if (isNoSuchServiceError(e)) {
if (isVersion27OrLater(monitor)) {
Map<String, ProjectInfo> projects = listProjects(monitor);
for (String projectName : projects.keySet()) {
result.add(new Project(new NameKey(projectName)));
}
} else {
// Gerrit <= 2.2.1
List<Project> projects = execute(monitor, new Operation<List<Project>>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getProjectAdminService(monitor).visibleProjects(this);
}
});
for (Project project : projects) {
ProjectDetailX projectDetail = new ProjectDetailX();
projectDetail.setProject(project);
if (!GerritUtil.isPermissionOnlyProject(projectDetail, gerritConfig)) {
result.add(project);
}
}
}
} else {
throw e;
}
}
Collections.sort(result, new ProjectByNameComparator());
return result;
}
private Map<String, ProjectInfo> listProjects(IProgressMonitor monitor) throws GerritException {
final String uri = "/projects/"; //$NON-NLS-1$
TypeToken<Map<String, ProjectInfo>> resultType = new TypeToken<Map<String, ProjectInfo>>() {
};
return executeGetRestRequest(uri, resultType.getType(), monitor);
}
private ProjectAdminService getProjectAdminService(IProgressMonitor monitor) {
return getService(ProjectAdminService.class, monitor);
}
public boolean isAnonymous() {
return client.isAnonymous();
}
protected void configurationChanged(GerritConfiguration config) {
}
protected void authStateChanged(GerritAuthenticationState config) {
}
protected <T> T execute(IProgressMonitor monitor, Operation<T> operation) throws GerritException {
try {
GerritRequest.setCurrentRequest(new GerritRequest(monitor));
try {
return executeOnce(monitor, operation);
} catch (GerritException e) {
if (isAuthenticationException(e)) {
operation.reset();
return executeOnce(monitor, operation);
}
throw e;
}
} finally {
GerritRequest.setCurrentRequest(null);
}
}
private <T> T executePostRestRequest(final String url, final Object input, final Type resultType,
final ErrorHandler handler, IProgressMonitor monitor) throws GerritException {
return execute(monitor, new Operation<T>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
try {
setResult(client.<T> postRestRequest(url, input, resultType, handler, monitor));
} catch (IOException e) {
throw new GerritException(e);
}
}
});
}
private <T> T executeGetRestRequest(final String url, final Type resultType, IProgressMonitor monitor)
throws GerritException {
return execute(monitor, new Operation<T>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
try {
setResult(client.<T> getRestRequest(url, resultType, monitor));
} catch (IOException e) {
throw new GerritException(e);
}
}
});
}
private <T> T executePutRestRequest(final String url, final Object input, final Type resultType,
final ErrorHandler handler, IProgressMonitor monitor) throws GerritException {
return execute(monitor, new Operation<T>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
try {
setResult(client.<T> putRestRequest(url, input, resultType, handler, monitor));
} catch (IOException e) {
throw new GerritException(e);
}
}
});
}
private <T> T executeDeleteRestRequest(final String url, final Object input, final Type resultType,
final ErrorHandler handler, IProgressMonitor monitor) throws GerritException {
return execute(monitor, new Operation<T>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
try {
setResult(client.<T> deleteRestRequest(url, input, resultType, handler, monitor));
} catch (IOException e) {
throw new GerritException(e);
}
}
});
}
private <T> T executeOnce(IProgressMonitor monitor, Operation<T> operation) throws GerritException {
operation.execute(monitor);
if (operation.getException() instanceof GerritException) {
throw (GerritException) operation.getException();
} else if (operation.getException() instanceof OperationCanceledException) {
throw (OperationCanceledException) operation.getException();
} else if (operation.getException() instanceof RuntimeException) {
throw (RuntimeException) operation.getException();
} else if (operation.getException() != null) {
GerritException e = new GerritException();
e.initCause(operation.getException());
throw e;
}
return operation.getResult();
}
protected synchronized <T extends RemoteJsonService> T getService(Class<T> clazz, IProgressMonitor monitor) {
Version version = Version.emptyVersion;
try {
version = getCachedVersion(monitor);
} catch (GerritException e) {
// ignore, continue with emptyVersion
}
RemoteJsonService service = serviceByClass.get(clazz);
if (service == null) {
service = GerritService.create(clazz, client, version);
serviceByClass.put(clazz, service);
}
return clazz.cast(service);
}
@Override
public AbstractRemoteEmfFactoryProvider<IRepository, IReview> createFactoryProvider() {
return new GerritRemoteFactoryProvider(this);
}
private Version getCachedVersion(IProgressMonitor monitor) throws GerritException {
synchronized (this) {
if (myVersion != null) {
return myVersion;
}
}
Version version = getVersion(monitor);
synchronized (this) {
myVersion = version;
}
return myVersion;
}
public Version getVersion(IProgressMonitor monitor) throws GerritException {
return execute(monitor, new Operation<Version>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
try {
Request<String> request = new Request<String>() {
@Override
public HttpMethodBase createMethod() throws IOException {
return new GetMethod(client.getUrl() + "/tools/hooks/"); //$NON-NLS-1$
}
@Override
public String process(HttpMethodBase method) throws IOException {
String content = method.getResponseBodyAsString();
Matcher matcher = GERRIT_VERSION_PATTERN.matcher(content);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}
};
String result = client.execute(request, false, monitor);
Version version = GerritVersion.parseGerritVersion(result);
onSuccess(version);
} catch (Exception e) {
onFailure(e);
}
}
});
}
public String toReviewId(String id, IProgressMonitor monitor) throws GerritException {
try {
Integer.parseInt(id);
return id;
} catch (NumberFormatException e) {
try {
List<GerritQueryResult> results = executeQuery(monitor, id);
if (results.size() != 1) {
throw new GerritException(NLS.bind("{0} is not a valid review ID", id)); //$NON-NLS-1$
}
return Integer.toString(results.get(0).getNumber());
} catch (GerritException e2) {
throw new GerritException(NLS.bind("{0} is not a valid review ID", id), e2); //$NON-NLS-1$
}
}
}
private static String encode(String string) throws GerritException {
try {
return URLEncoder.encode(string, "UTF-8"); //$NON-NLS-1$
} catch (UnsupportedEncodingException e) {
throw new GerritException(e);
}
}
public VoidResult setStarred(final String reviewId, final boolean starred, IProgressMonitor monitor)
throws GerritException {
final Change.Id id = new Change.Id(id(reviewId));
final ToggleStarRequest req = new ToggleStarRequest();
req.toggle(id, starred);
if (isVersion28OrLater(monitor)) {
final String uri = "/a/accounts/self/starred.changes/" + id.get(); //$NON-NLS-1$
return execute(monitor, new Operation<VoidResult>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
if (starred) {
executePutRestRequest(uri, req, ToggleStarRequest.class, createErrorHandler(), monitor);
} else {
executeDeleteRestRequest(uri, req, ToggleStarRequest.class, createErrorHandler(), monitor);
}
}
});
} else {
return execute(monitor, new Operation<VoidResult>() {
@Override
public void execute(IProgressMonitor monitor) throws GerritException {
getChangeListService(monitor).toggleStars(req, this);
}
});
}
}
private ErrorHandler createErrorHandler() {
return new ErrorHandler() {
@Override
public void handleError(HttpMethodBase method) throws GerritException {
throw new GerritException(method.getStatusLine().getReasonPhrase());
}
};
}
}