/******************************************************************************* * Copyright (c) 2015 Tasktop Technologies 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: * Tasktop Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.gerrit.core.client; import java.io.IOException; import java.lang.reflect.Type; import java.util.List; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.mylyn.internal.gerrit.core.client.GerritHttpClient.ErrorHandler; import org.eclipse.mylyn.internal.gerrit.core.client.GerritService.GerritRequest; import org.eclipse.mylyn.internal.gerrit.core.client.data.GerritQueryResult; import com.google.common.collect.ImmutableList; import com.google.gson.reflect.TypeToken; import com.google.gwt.user.client.rpc.AsyncCallback; public class GerritRestClient { static abstract class Operation<T> implements AsyncCallback<T> { private Throwable exception; private T result; private final GerritHttpClient client; public abstract void execute(IProgressMonitor monitor) throws GerritException; public Operation(GerritHttpClient client) { this.client = client; } 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; } } protected static final String GET_LABELS_OPTION = "LABELS"; //$NON-NLS-1$ protected static final String GET_DETAILED_ACCOUNTS_OPTION = "DETAILED_ACCOUNTS"; //$NON-NLS-1$ private final GerritHttpClient client; public GerritRestClient(GerritHttpClient client) { this.client = client; } private static 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; } 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); } } protected <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>(client) { @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); } } }); } protected <T> T executeGetRestRequest(final String url, final Type resultType, IProgressMonitor monitor) throws GerritException { return execute(monitor, new Operation<T>(client) { @Override public void execute(IProgressMonitor monitor) throws GerritException { try { setResult(client.<T> getRestRequest(url, resultType, monitor)); } catch (IOException e) { throw new GerritException(e); } } }); } protected <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>(client) { @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); } } }); } public <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>(client) { @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(); } /** * 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, ImmutableList.of(GET_LABELS_OPTION, GET_DETAILED_ACCOUNTS_OPTION)); } /** * 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 optionsList * List of query options ("&o=" parameter). Only applicable for the REST API, ignored otherwise. May be * null or empty. * @return a list of GerritQueryResults built from the parsed query result (ChangeInfo:s) * @throws GerritException */ public List<GerritQueryResult> executeQuery(IProgressMonitor monitor, final String queryString, List<String> optionsList) throws GerritException { String uri = "/changes/?q=" + GerritClient.encode(queryString); //$NON-NLS-1$ if (optionsList != null && !optionsList.isEmpty()) { for (String option : optionsList) { if (StringUtils.isNotBlank(option)) { uri += "&o=" + GerritClient.encode(option); //$NON-NLS-1$ } } } TypeToken<List<GerritQueryResult>> queryResultListType = new TypeToken<List<GerritQueryResult>>() { }; return executeGetRestRequest(uri, queryResultListType.getType(), monitor); } /** * 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 { return executeQuery(monitor, "owner:self OR reviewer:self"); //$NON-NLS-1$ } /** * 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$ } }