blob: 55afa1201f9774cd06fa8d879fa7df82b0701088 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 Frank Becker and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Frank Becker - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.bugzilla.rest.core;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.core.operations.IOperationMonitor;
import org.eclipse.mylyn.commons.repositories.core.RepositoryLocation;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationType;
import org.eclipse.mylyn.commons.repositories.core.auth.UserCredentials;
import org.eclipse.mylyn.commons.repositories.http.core.CommonHttpClient;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.BooleanResult;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.BugzillaRestIdResult;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.Field;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.FieldResponse;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.LoginToken;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.Named;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.ParameterResponse;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.Product;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.ProductResponse;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.PutUpdateResult;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.RestResponse;
import org.eclipse.mylyn.internal.bugzilla.rest.core.response.data.VersionResponse;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.RepositoryResponse;
import org.eclipse.mylyn.tasks.core.RepositoryResponse.ResponseKind;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.AbstractTaskAttachmentSource;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskDataCollector;
import org.eclipse.osgi.util.NLS;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.reflect.TypeToken;
public class BugzillaRestClient {
private final CommonHttpClient client;
private final BugzillaRestConnector connector;
public static final int MAX_RETRIEVED_PER_QUERY = 50;
public BugzillaRestClient(RepositoryLocation location, BugzillaRestConnector connector) {
client = new CommonHttpClient(location);
this.connector = connector;
}
public CommonHttpClient getClient() {
return client;
}
public BugzillaRestVersion getVersion(IOperationMonitor monitor) throws BugzillaRestException {
VersionResponse versionResponse = new BugzillaRestUnauthenticatedGetRequest<VersionResponse>(client, "/version", //$NON-NLS-1$
new TypeToken<VersionResponse>() {
}).run(monitor);
return new BugzillaRestVersion(versionResponse.getVersion());
}
public boolean validate(IOperationMonitor monitor) throws BugzillaRestException {
RepositoryLocation location = getClient().getLocation();
if (location.getBooleanPropery(IBugzillaRestConstants.REPOSITORY_USE_API_KEY)) {
UserCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY);
Preconditions.checkState(credentials != null, "Authentication requested without valid credentials");
String url = MessageFormat.format("/valid_login?login={0}&api_key={1}", //$NON-NLS-1$
credentials.getUserName(), location.getProperty(IBugzillaRestConstants.REPOSITORY_API_KEY));
BooleanResult response = new BugzillaRestUnauthenticatedGetRequest<BooleanResult>(client, url,
new TypeToken<BooleanResult>() {
}).run(monitor);
return response.getResult();
} else {
LoginToken validateResponse = new BugzillaRestLoginRequest(client).run(monitor);
if (validateResponse != null && !Strings.isNullOrEmpty(validateResponse.getId())) {
// invalide the token
String url = MessageFormat.format("/logout?token={0}", //$NON-NLS-1$
validateResponse.getToken());
new BugzillaRestUnauthenticatedGetRequest<BooleanResult>(client, url, new TypeToken<BooleanResult>() {
}).run(monitor);
return true;
}
}
return false;
}
public BugzillaRestConfiguration getConfiguration(TaskRepository repository, IOperationMonitor monitor) {
try {
BugzillaRestConfiguration config = new BugzillaRestConfiguration(repository.getUrl());
config.setFields(getFields(monitor));
config.setProducts(getProducts(monitor));
ParameterResponse parameterResponse = getParameters(monitor);
config.setParameters(parameterResponse != null ? parameterResponse.getParameters() : null);
return config;
} catch (Exception e) {
StatusHandler
.log(new Status(IStatus.ERROR, BugzillaRestCore.ID_PLUGIN, "Could not get the Configuration", e)); //$NON-NLS-1$
return null;
}
}
public <R extends RestResponse<E>, E extends Named> Map<String, E> retrieveItems(IOperationMonitor monitor,
String path, TypeToken<?> typeToken) throws BugzillaRestException {
R response = new BugzillaRestGetRequest<R>(client, path, typeToken).run(monitor);
E[] members = response.getArray();
return Maps.uniqueIndex(Lists.newArrayList(members), new Function<E, String>() {
public String apply(E input) {
return input.getName();
};
});
}
private Map<String, Field> getFields(IOperationMonitor monitor) throws BugzillaRestException {
return retrieveItems(monitor, "/field/bug?", new TypeToken<FieldResponse>() { //$NON-NLS-1$
});
}
private Map<String, Product> getProducts(IOperationMonitor monitor) throws BugzillaRestException {
return retrieveItems(monitor, "/product?type=accessible", new TypeToken<ProductResponse>() { //$NON-NLS-1$
});
}
public ParameterResponse getParameters(IOperationMonitor monitor) throws BugzillaRestException {
return new BugzillaRestGetRequest<ParameterResponse>(client, "/parameters?", //$NON-NLS-1$
new TypeToken<ParameterResponse>() {
}).run(monitor);
}
public RepositoryResponse postTaskData(TaskData taskData, Set<TaskAttribute> oldAttributes,
IOperationMonitor monitor) throws BugzillaRestException {
if (taskData.isNew()) {
BugzillaRestIdResult result = new BugzillaRestPostNewTask(client, taskData).run(monitor);
return new RepositoryResponse(ResponseKind.TASK_CREATED, result.getId());
} else {
PutUpdateResult result = new BugzillaRestPutUpdateTask(client, taskData, oldAttributes).run(monitor);
return new RepositoryResponse(ResponseKind.TASK_UPDATED, result.getBugs()[0].getId());
}
}
private final Function<String, String> removeLeadingZero = new Function<String, String>() {
@Override
public String apply(String input) {
while (input.startsWith("0")) { //$NON-NLS-1$
input = input.substring(1);
}
return input;
}
};
public void getTaskData(Set<String> taskIds, TaskRepository taskRepository, TaskDataCollector collector,
IOperationMonitor monitor) throws BugzillaRestException {
BugzillaRestConfiguration config;
try {
config = connector.getRepositoryConfiguration(taskRepository);
} catch (CoreException e1) {
throw new BugzillaRestException(e1);
}
Iterable<String> taskIdsTemp = Iterables.transform(taskIds, removeLeadingZero);
Iterable<List<String>> partitions = Iterables.partition(taskIdsTemp, MAX_RETRIEVED_PER_QUERY);
for (List<String> list : partitions) {
Joiner joiner = Joiner.on(",id=").skipNulls(); //$NON-NLS-1$
String urlIDList = "id=" + joiner.join(list); //$NON-NLS-1$
try {
List<TaskData> taskDataArray = new BugzillaRestGetTaskData(client, connector, urlIDList, taskRepository)
.run(monitor);
for (TaskData taskData : taskDataArray) {
new BugzillaRestGetTaskComments(getClient(), taskData).run(monitor);
new BugzillaRestGetTaskAttachments(getClient(), taskData).run(monitor);
config.updateProductOptions(taskData);
config.addValidOperations(taskData);
config.updateFlags(taskData);
config.updateKeyword(taskData);
collector.accept(taskData);
}
} catch (RuntimeException e) {
// if the Throwable was warped in a RuntimeException in
// BugzillaRestGetTaskData.JSonTaskDataDeserializer.deserialize()
// we now remove the warper and throw a BugzillaRestException
Throwable cause = e.getCause();
if (cause instanceof CoreException) {
throw new BugzillaRestException(cause);
}
throw e;
}
}
}
public IStatus performQuery(TaskRepository taskRepository, final IRepositoryQuery query,
final TaskDataCollector resultCollector, IOperationMonitor monitor) throws BugzillaRestException {
String urlIDList = query.getUrl();
urlIDList = urlIDList.substring(urlIDList.indexOf("?") + 1); //$NON-NLS-1$
List<TaskData> taskDataArray = new BugzillaRestGetTaskData(client, connector, urlIDList, taskRepository)
.run(monitor);
for (final TaskData taskData : taskDataArray) {
taskData.setPartial(true);
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
resultCollector.accept(taskData);
}
@Override
public void handleException(Throwable exception) {
StatusHandler.log(new Status(IStatus.ERROR, BugzillaRestCore.ID_PLUGIN,
NLS.bind("Unexpected error during result collection. TaskID {0} in repository {1}", //$NON-NLS-1$
taskData.getTaskId(), taskData.getRepositoryUrl()),
exception));
}
});
}
return Status.OK_STATUS;
}
public InputStream getAttachmentData(@NonNull TaskAttribute attachmentAttribute,
@Nullable IOperationMonitor monitor) throws BugzillaRestException {
return new BugzillaRestGetTaskAttachmentData(client, attachmentAttribute).run(monitor);
}
public void addAttachment(String bugReportID, String comment, AbstractTaskAttachmentSource source,
TaskAttribute attachmentAttribute, IOperationMonitor monitor) throws BugzillaRestException {
new BugzillaRestPostNewAttachment(client, bugReportID, comment, source, attachmentAttribute, monitor)
.run(monitor);
}
}