blob: 4270254efd4ac15e33e6a47e0ab3c7abf35e2898 [file] [log] [blame]
package com.intellij.tasks.jira;
import com.google.gson.JsonObject;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.tasks.Task;
import com.intellij.tasks.TaskState;
import com.intellij.tasks.impl.BaseRepositoryImpl;
import com.intellij.tasks.jira.model.JiraIssue;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.annotations.Tag;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.util.List;
/**
* @author Dmitry Avdeev
*/
@Tag("JIRA")
public class JiraRepository extends BaseRepositoryImpl {
private final static Logger LOG = Logger.getInstance("#com.intellij.tasks.jira.JiraRepository");
public static final String LOGIN_FAILED_CHECK_YOUR_PERMISSIONS = "Login failed. Check your permissions.";
public static final String REST_API_PATH_SUFFIX = "/rest/api/latest";
/**
* Default JQL query
*/
private String mySearchQuery = "assignee = currentUser() and resolution = Unresolved order by updated";
private JiraRestApi myRestApiVersion;
/**
* Serialization constructor
*/
@SuppressWarnings({"UnusedDeclaration"})
public JiraRepository() {
}
public JiraRepository(JiraRepositoryType type) {
super(type);
}
private JiraRepository(JiraRepository other) {
super(other);
mySearchQuery = other.mySearchQuery;
}
@Override
public boolean equals(Object o) {
if (!super.equals(o)) return false;
if (o.getClass() != getClass()) return false;
return Comparing.equal(mySearchQuery, ((JiraRepository)o).mySearchQuery);
}
/**
* Always use Basic HTTP authentication for JIRA REST interface
*/
@Override
public boolean isUseHttpAuthentication() {
return true;
}
public Task[] getIssues(@Nullable String query, int max, long since) throws Exception {
if (myRestApiVersion == null) {
myRestApiVersion = discoverRestApiVersion();
}
String jqlQuery = mySearchQuery;
if (!StringUtil.isEmpty(query)) {
jqlQuery = String.format("summary ~ '%s'", query);
if (!StringUtil.isEmpty(mySearchQuery)) {
jqlQuery += String.format(" and %s", mySearchQuery);
}
}
List<JiraIssue> issues = myRestApiVersion.findIssues(jqlQuery, max);
return ContainerUtil.map2Array(issues, Task.class, new Function<JiraIssue, Task>() {
@Override
public JiraTask fun(JiraIssue issue) {
return new JiraTask(issue, JiraRepository.this);
}
});
}
@Nullable
@Override
public Task findTask(String id) throws Exception {
if (myRestApiVersion == null) {
myRestApiVersion = discoverRestApiVersion();
}
JiraIssue issue = myRestApiVersion.findIssue(id);
return issue == null ? null : new JiraTask(issue, this);
}
@Nullable
@Override
public CancellableConnection createCancellableConnection() {
String uri = getUrl() + REST_API_PATH_SUFFIX + "/search?maxResults=1&jql=" + encodeUrl(mySearchQuery);
return new HttpTestConnection<GetMethod>(new GetMethod(uri)) {
@Override
public void doTest(GetMethod method) throws Exception {
executeMethod(method);
}
};
}
public JiraRepository clone() {
return new JiraRepository(this);
}
@Override
protected int getFeatures() {
return super.getFeatures() | TIME_MANAGEMENT;
}
public String getSearchQuery() {
return mySearchQuery;
}
public void setSearchQuery(String searchQuery) {
mySearchQuery = searchQuery;
}
@NotNull
public JiraRestApi discoverRestApiVersion() throws Exception {
String responseBody;
try {
responseBody = executeMethod(new GetMethod(getUrl() + REST_API_PATH_SUFFIX + "/serverInfo"));
}
catch (Exception e) {
LOG.warn("Can't find out JIRA REST API version");
throw e;
}
JsonObject object = JiraUtil.GSON.fromJson(responseBody, JsonObject.class);
// when JIRA 4.x support will be dropped 'versionNumber' array in response
// may be used instead version string parsing
return JiraRestApi.fromJiraVersion(object.get("version").getAsString(), this);
}
@Override
public void setTaskState(Task task, TaskState state) throws Exception {
myRestApiVersion.setTaskState(task, state);
}
@NotNull
public String executeMethod(HttpMethod method) throws Exception {
LOG.debug("URI is " + method.getURI());
int statusCode;
String entityContent;
try {
statusCode = getHttpClient().executeMethod(method);
LOG.debug("Status code is " + statusCode);
// may be null if 204 No Content received
final InputStream stream = method.getResponseBodyAsStream();
entityContent = stream == null ? "" : StreamUtil.readText(stream, "utf-8");
LOG.debug(entityContent);
}
finally {
method.releaseConnection();
}
// besides SC_OK, can also be SC_NO_CONTENT in issue transition requests
// see: JiraRestApi#setTaskStatus
if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_NO_CONTENT) {
//if (statusCode >= 200 && statusCode < 300) {
return entityContent;
}
else if (method.getResponseHeader("Content-Type") != null) {
Header header = method.getResponseHeader("Content-Type");
if (header.getValue().startsWith("application/json")) {
JsonObject object = JiraUtil.GSON.fromJson(entityContent, JsonObject.class);
if (object.has("errorMessages")) {
String reason = StringUtil.join(object.getAsJsonArray("errorMessages"), " ");
// something meaningful to user, e.g. invalid field name in JQL query
LOG.warn(reason);
throw new Exception("Request failed. Reason: " + reason);
}
}
}
if (method.getResponseHeader("X-Authentication-Denied-Reason") != null) {
Header header = method.getResponseHeader("X-Authentication-Denied-Reason");
// only in JIRA >= 5.x.x
if (header.getValue().startsWith("CAPTCHA_CHALLENGE")) {
throw new Exception("Login failed. Enter captcha in web-interface.");
}
}
if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
throw new Exception(LOGIN_FAILED_CHECK_YOUR_PERMISSIONS);
}
throw new Exception("Request failed with HTTP error: " + HttpStatus.getStatusText(method.getStatusCode()));
}
@Override
public void setUrl(String url) {
myRestApiVersion = null;
super.setUrl(url);
}
}