summaryrefslogtreecommitdiff
path: root/plugins/tasks/tasks-core
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/tasks/tasks-core')
-rw-r--r--plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/JiraRepository.java41
-rw-r--r--plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskCombo.java2
-rw-r--r--plugins/tasks/tasks-core/src/com/intellij/tasks/gitlab/GitlabRepository.java24
-rw-r--r--plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java42
-rw-r--r--plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java76
-rw-r--r--plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/model/RedmineIssue.java4
6 files changed, 121 insertions, 68 deletions
diff --git a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/JiraRepository.java b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/JiraRepository.java
index b8004ca70023..fa80fb161188 100644
--- a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/JiraRepository.java
+++ b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/JiraRepository.java
@@ -12,7 +12,6 @@ import com.intellij.tasks.Task;
import com.intellij.tasks.TaskBundle;
import com.intellij.tasks.TaskState;
import com.intellij.tasks.impl.BaseRepositoryImpl;
-import com.intellij.tasks.impl.TaskUtil;
import com.intellij.tasks.impl.gson.GsonUtil;
import com.intellij.tasks.jira.rest.JiraRestApi;
import com.intellij.tasks.jira.soap.JiraLegacyApi;
@@ -52,6 +51,7 @@ public class JiraRepository extends BaseRepositoryImpl {
private static final boolean REDISCOVER_API = Boolean.getBoolean("tasks.jira.rediscover.api");
public static final Pattern JIRA_ID_PATTERN = Pattern.compile("\\p{javaUpperCase}+-\\d+");
+ public static final String AUTH_COOKIE_NAME = "JSESSIONID";
/**
* Default JQL query
@@ -145,12 +145,13 @@ public class JiraRepository extends BaseRepositoryImpl {
@Nullable
@Override
public CancellableConnection createCancellableConnection() {
+ clearCookies();
// TODO cancellable connection for XML_RPC?
return new CancellableConnection() {
@Override
protected void doTest() throws Exception {
ensureApiVersionDiscovered();
- myApiVersion.findTasks("", 1);
+ myApiVersion.findTasks(mySearchQuery, 1);
}
@Override
@@ -226,20 +227,18 @@ public class JiraRepository extends BaseRepositoryImpl {
HttpClient client = getHttpClient();
// Fix for https://jetbrains.zendesk.com/agent/#/tickets/24566
// See https://confluence.atlassian.com/display/ONDEMANDKB/Getting+randomly+logged+out+of+OnDemand for details
- if (BASIC_AUTH_ONLY) {
+ // IDEA-128824, IDEA-128706 Use cookie authentication only for JIRA on-Demand
+ // TODO Make JiraVersion more suitable for such checks
+ final boolean isJiraOnDemand = StringUtil.notNullize(myJiraVersion).contains("OD");
+ if (isJiraOnDemand) {
+ LOG.info("Connecting to JIRA on-Demand. Cookie authentication is enabled unless 'tasks.jira.basic.auth.only' VM flag is used.");
+ }
+ if (BASIC_AUTH_ONLY || !isJiraOnDemand) {
// to override persisted settings
setUseHttpAuthentication(true);
}
else {
- boolean cookieAuthenticated = false;
- for (Cookie cookie : client.getState().getCookies()) {
- if (cookie.getName().equals("JSESSIONID") && !cookie.isExpired()) {
- cookieAuthenticated = true;
- break;
- }
- }
- // disable subsequent basic authorization attempts if user already was authenticated
- boolean enableBasicAuthentication = !(isRestApiSupported() && cookieAuthenticated);
+ boolean enableBasicAuthentication = !(isRestApiSupported() && containsCookie(client, AUTH_COOKIE_NAME));
if (enableBasicAuthentication != isUseHttpAuthentication()) {
LOG.info("Basic authentication for subsequent requests was " + (enableBasicAuthentication ? "enabled" : "disabled"));
}
@@ -251,14 +250,15 @@ public class JiraRepository extends BaseRepositoryImpl {
// may be null if 204 No Content received
final InputStream stream = method.getResponseBodyAsStream();
String entityContent = stream == null ? "" : StreamUtil.readText(stream, CharsetToolkit.UTF8);
- TaskUtil.prettyFormatJsonToLog(LOG, entityContent);
+ //TaskUtil.prettyFormatJsonToLog(LOG, entityContent);
// 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) {
+ clearCookies();
+ if (method.getResponseHeader("Content-Type") != null) {
Header header = method.getResponseHeader("Content-Type");
if (header.getValue().startsWith("application/json")) {
JsonObject object = GSON.fromJson(entityContent, JsonObject.class);
@@ -284,6 +284,19 @@ public class JiraRepository extends BaseRepositoryImpl {
throw new Exception(TaskBundle.message("failure.http.error", statusCode, statusText));
}
+ private static boolean containsCookie(@NotNull HttpClient client, @NotNull String cookieName) {
+ for (Cookie cookie : client.getState().getCookies()) {
+ if (cookie.getName().equals(cookieName) && !cookie.isExpired()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void clearCookies() {
+ getHttpClient().getState().clearCookies();
+ }
+
// Made public for SOAP API compatibility
@Override
public HttpClient getHttpClient() {
diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskCombo.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskCombo.java
index f053f5012969..622c47dc552c 100644
--- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskCombo.java
+++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskCombo.java
@@ -81,7 +81,7 @@ public class SwitchTaskCombo extends ComboBoxAction implements DumbAware {
public void update(AnActionEvent e) {
Project project = e.getData(CommonDataKeys.PROJECT);
Presentation presentation = e.getPresentation();
- if (project == null || project.isDisposed() || (ActionPlaces.MAIN_MENU.equals(e.getPlace()) && findFrame(e) == null)) {
+ if (project == null || project.isDisposed() || (ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) && findFrame(e) == null)) {
presentation.setEnabled(false);
presentation.setText("");
presentation.setIcon(null);
diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/gitlab/GitlabRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/gitlab/GitlabRepository.java
index cc66448c94e9..9df0eb07b26e 100644
--- a/plugins/tasks/tasks-core/src/com/intellij/tasks/gitlab/GitlabRepository.java
+++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/gitlab/GitlabRepository.java
@@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.intellij.openapi.util.Comparing;
import com.intellij.tasks.Task;
-import com.intellij.tasks.TaskBundle;
import com.intellij.tasks.TaskRepositoryType;
import com.intellij.tasks.gitlab.model.GitlabIssue;
import com.intellij.tasks.gitlab.model.GitlabProject;
@@ -14,7 +13,9 @@ import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.annotations.Tag;
import com.intellij.util.xmlb.annotations.Transient;
-import org.apache.http.*;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.protocol.HttpContext;
@@ -118,24 +119,7 @@ public class GitlabRepository extends NewBaseRepositoryImpl {
@Nullable
@Override
public CancellableConnection createCancellableConnection() {
- return new CancellableConnection() {
- private HttpGet myRequest = new HttpGet(getIssuesUrl());
-
- @Override
- protected void doTest() throws Exception {
- HttpResponse response = getHttpClient().execute(myRequest);
- StatusLine statusLine = response.getStatusLine();
- if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
- throw new Exception(TaskBundle.message("failure.http.error", statusLine.getStatusCode(), statusLine.getReasonPhrase()));
- }
- }
-
- // TODO: find more about proper request aborting in HttpClient4.x
- @Override
- public void cancel() {
- myRequest.abort();
- }
- };
+ return new HttpTestConnection(new HttpGet(getIssuesUrl()));
}
/**
diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java
index c1648fc8f490..082d49812d5b 100644
--- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java
+++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java
@@ -3,6 +3,7 @@ package com.intellij.tasks.impl.httpclient;
import com.intellij.tasks.TaskRepositoryType;
import com.intellij.tasks.config.TaskSettings;
import com.intellij.tasks.impl.BaseRepository;
+import com.intellij.tasks.impl.RequestFailedException;
import com.intellij.tasks.impl.TaskUtil;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.net.ssl.CertificateManager;
@@ -14,6 +15,7 @@ import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.auth.BasicScheme;
@@ -154,4 +156,44 @@ public abstract class NewBaseRepositoryImpl extends BaseRepository {
}
}
}
+
+ public class HttpTestConnection extends CancellableConnection {
+
+ // Request can be changed during test
+ protected volatile HttpRequestBase myCurrentRequest;
+
+ public HttpTestConnection(@NotNull HttpRequestBase request) {
+ myCurrentRequest = request;
+ }
+
+ @Override
+ protected void doTest() throws Exception {
+ try {
+ test();
+ }
+ catch (IOException e) {
+ // Depending on request state AbstractExecutionAwareRequest.abort() can cause either
+ // * RequestAbortedException if connection was not yet leased
+ // * InterruptedIOException before reading response
+ // * SocketException("Socket closed") during reading response
+ // However in all cases 'aborted' flag should be properly set
+ if (!myCurrentRequest.isAborted()) {
+ throw e;
+ }
+ }
+ }
+
+ protected void test() throws Exception {
+ HttpResponse response = getHttpClient().execute(myCurrentRequest);
+ StatusLine statusLine = response.getStatusLine();
+ if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
+ throw RequestFailedException.forStatusCode(statusLine.getStatusCode(), statusLine.getReasonPhrase());
+ }
+ }
+
+ @Override
+ public void cancel() {
+ myCurrentRequest.abort();
+ }
+ }
}
diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java
index e63b2b365e60..a4b10386aeba 100644
--- a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java
+++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java
@@ -16,6 +16,7 @@ import com.intellij.util.xmlb.annotations.Tag;
import com.intellij.util.xmlb.annotations.Transient;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
+import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
@@ -23,6 +24,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -107,28 +110,36 @@ public class RedmineRepository extends NewBaseRepositoryImpl {
return new RedmineRepository(this);
}
+ @Nullable
@Override
- public void testConnection() throws Exception {
- // Strangely, Redmine doesn't return 401 or 403 error codes, if client sent wrong credentials, and instead
- // merely returns empty array of issues with status code of 200. This means that we should attempt to fetch
- // something more specific than issues to test proper configuration, e.g. current user information at
- // /users/current.json. Unfortunately this endpoint may be unavailable on some old servers (see IDEA-122845)
- // and in this case we have to come back to requesting issues in this case to test anything at all.
- HttpClient client = getHttpClient();
- URIBuilder uriBuilder = new URIBuilder(getRestApiUrl("users", "current.json"));
- if (isUseApiKeyAuthentication()) {
- uriBuilder.addParameter("key", getAPIKey());
- }
- HttpResponse response = client.execute(new HttpGet(uriBuilder.build()));
- //TaskUtil.prettyFormatResponseToLog(LOG, response);
- int code = response.getStatusLine().getStatusCode();
- if (code == HttpStatus.SC_NOT_FOUND) {
- getIssues("", 0, 1, true);
- return;
- }
- if (code != HttpStatus.SC_OK) {
- throw RequestFailedException.forStatusCode(code);
- }
+ public CancellableConnection createCancellableConnection() {
+ return new NewBaseRepositoryImpl.HttpTestConnection(new HttpGet()) {
+ @Override
+ protected void test() throws Exception {
+ // Strangely, Redmine doesn't return 401 or 403 error codes, if client sent wrong credentials, and instead
+ // merely returns empty array of issues with status code of 200. This means that we should attempt to fetch
+ // something more specific than issues to test proper configuration, e.g. current user information at
+ // /users/current.json. Unfortunately this endpoint may be unavailable on some old servers (see IDEA-122845)
+ // and in this case we have to come back to requesting issues in this case to test anything at all.
+
+ URIBuilder uriBuilder = new URIBuilder(getRestApiUrl("users", "current.json"));
+ if (isUseApiKeyAuthentication()) {
+ uriBuilder.addParameter("key", getAPIKey());
+ }
+ myCurrentRequest.setURI(uriBuilder.build());
+ HttpClient client = getHttpClient();
+
+ HttpResponse httpResponse = client.execute(myCurrentRequest);
+ StatusLine statusLine = httpResponse.getStatusLine();
+ if (statusLine != null && statusLine.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
+ myCurrentRequest = new HttpGet(getIssuesUrl(0, 1, true));
+ statusLine = client.execute(myCurrentRequest).getStatusLine();
+ }
+ if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
+ throw RequestFailedException.forStatusCode(statusLine.getStatusCode(), statusLine.getReasonPhrase());
+ }
+ }
+ };
}
@Override
@@ -144,12 +155,6 @@ public class RedmineRepository extends NewBaseRepositoryImpl {
public List<RedmineIssue> fetchIssues(String query, int offset, int limit, boolean withClosed) throws Exception {
ensureProjectsDiscovered();
- URIBuilder builder = new URIBuilder(getRestApiUrl("issues.json"))
- .addParameter("offset", String.valueOf(offset))
- .addParameter("limit", String.valueOf(limit))
- .addParameter("status_id", withClosed ? "*" : "open")
- .addParameter("assigned_to_id", "me");
-
// Legacy API, can't find proper documentation
//if (StringUtil.isNotEmpty(query)) {
// builder.addParameter("fields[]", "subject").addParameter("operators[subject]", "~").addParameter("values[subject][]", query);
@@ -158,15 +163,24 @@ public class RedmineRepository extends NewBaseRepositoryImpl {
//if (myCurrentProject != null && myCurrentProject != UNSPECIFIED_PROJECT) {
// builder.addParameter("project_id", String.valueOf(myCurrentProject.getId()));
//}
- if (isUseApiKeyAuthentication()) {
- builder.addParameter("key", myAPIKey);
- }
HttpClient client = getHttpClient();
- HttpGet method = new HttpGet(builder.toString());
+ HttpGet method = new HttpGet(getIssuesUrl(offset, limit, withClosed));
IssuesWrapper wrapper = client.execute(method, new GsonSingleObjectDeserializer<IssuesWrapper>(GSON, IssuesWrapper.class));
return wrapper == null ? Collections.<RedmineIssue>emptyList() : wrapper.getIssues();
}
+ private URI getIssuesUrl(int offset, int limit, boolean withClosed) throws URISyntaxException {
+ URIBuilder builder = new URIBuilder(getRestApiUrl("issues.json"))
+ .addParameter("offset", String.valueOf(offset))
+ .addParameter("limit", String.valueOf(limit))
+ .addParameter("status_id", withClosed ? "*" : "open")
+ .addParameter("assigned_to_id", "me");
+ if (isUseApiKeyAuthentication()) {
+ builder.addParameter("key", myAPIKey);
+ }
+ return builder.build();
+ }
+
public List<RedmineProject> fetchProjects() throws Exception {
HttpClient client = getHttpClient();
// Download projects with pagination (IDEA-125056, IDEA-125157)
diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/model/RedmineIssue.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/model/RedmineIssue.java
index 07b5669a1540..aea41bdb6ca8 100644
--- a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/model/RedmineIssue.java
+++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/model/RedmineIssue.java
@@ -19,7 +19,7 @@ public class RedmineIssue {
private IssueStatus status;
@Mandatory
private String subject;
- @Mandatory
+ // IDEA-126470 May be missing if issue was not created via web-interface
private String description;
@SerializedName("done_ratio")
private int doneRatio;
@@ -45,7 +45,7 @@ public class RedmineIssue {
return subject;
}
- @NotNull
+ @Nullable
public String getDescription() {
return description;
}