Skip to content

Commit

Permalink
Avoid slow operation
Browse files Browse the repository at this point in the history
  • Loading branch information
damien-urruty-sonarsource committed Nov 22, 2023
1 parent 0b10fe0 commit 421df9e
Show file tree
Hide file tree
Showing 17 changed files with 258 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -391,9 +391,9 @@ object SonarLintIntelliJClient : SonarLintClient {

override fun getCredentials(params: GetCredentialsParams): CompletableFuture<GetCredentialsResponse> {
val connectionId = params.connectionId
return ServerConnectionService.getInstance().getServerConnectionByName(connectionId)
.map { connection -> connection.credentials.token?.let { CompletableFuture.completedFuture(GetCredentialsResponse(TokenDto(it))) }
?: connection.credentials.login?.let { CompletableFuture.completedFuture(GetCredentialsResponse(UsernamePasswordDto(it, connection.credentials.password))) }
return ServerConnectionService.getInstance().getServerCredentialsByName(connectionId)
.map { credentials -> credentials.token?.let { CompletableFuture.completedFuture(GetCredentialsResponse(TokenDto(it))) }
?: credentials.login?.let { CompletableFuture.completedFuture(GetCredentialsResponse(UsernamePasswordDto(it, credentials.password))) }
?: CompletableFuture.failedFuture(IllegalArgumentException("Invalid credentials for connection: $connectionId"))}
.orElseGet { CompletableFuture.failedFuture(IllegalArgumentException("Connection '$connectionId' not found")) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ sealed class ServerConnection {
abstract val name: String
abstract val notificationsDisabled: Boolean
abstract val hostUrl: String
abstract val credentials: ServerConnectionCredentials
abstract val product: SonarProduct
abstract val links: ServerLinks
abstract val endpointParams: EndpointParams
Expand All @@ -45,14 +44,13 @@ sealed class ServerConnection {
val productName get() = product.productName
}

data class SonarQubeConnection(override val name: String, override val hostUrl: String, override val credentials: ServerConnectionCredentials, override val notificationsDisabled: Boolean) : ServerConnection() {
data class SonarQubeConnection(override val name: String, override val hostUrl: String, override val notificationsDisabled: Boolean) : ServerConnection() {
override val product = SonarProduct.SONARQUBE
override val links = SonarQubeLinks(hostUrl)
override val endpointParams = EndpointParams(hostUrl, false, null)
}

data class SonarCloudConnection(override val name: String, val token: String, val organizationKey: String, override val notificationsDisabled: Boolean) : ServerConnection() {
override val credentials = ServerConnectionCredentials(null, null, token)
data class SonarCloudConnection(override val name: String, val organizationKey: String, override val notificationsDisabled: Boolean) : ServerConnection() {
override val product = SonarProduct.SONARCLOUD
override val links = SonarCloudLinks
override val hostUrl: String = SONARCLOUD_URL
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.sonarlint.intellij.config.global

class ServerConnectionCredentialsNotFound(connectionName: String) : RuntimeException("Unable to load credentials for connection '$connectionName'")
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.sonarlint.intellij.config.global;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.Messages;
Expand All @@ -36,8 +37,10 @@
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -53,6 +56,8 @@

import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
import static org.sonarlint.intellij.config.Settings.getSettingsFor;
import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;

public class ServerConnectionMgmtPanel implements ConfigurationPanel<SonarLintGlobalSettings> {
private static final String LABEL_NO_SERVERS = "Add a connection to SonarQube or SonarCloud";
Expand All @@ -64,8 +69,10 @@ public class ServerConnectionMgmtPanel implements ConfigurationPanel<SonarLintGl

// Model
private GlobalConfigurationListener connectionChangeListener;
private final List<ServerConnection> connections = new ArrayList<>();
private final Map<String, ServerConnectionWithAuth> updatedConnectionsByName = new HashMap<>();
private final Map<String, ServerConnectionWithAuth> addedConnectionsByName = new HashMap<>();
private final Set<String> deletedServerIds = new HashSet<>();
private CollectionListModel<ServerConnection> listModel;

private void create() {
var app = ApplicationManager.getApplication();
Expand Down Expand Up @@ -139,29 +146,35 @@ public JComponent getComponent() {

@Override
public boolean isModified(SonarLintGlobalSettings settings) {
return !connections.equals(ServerConnectionService.getInstance().getConnections());
return !updatedConnectionsByName.isEmpty() || !addedConnectionsByName.isEmpty() || !deletedServerIds.isEmpty();
}

@Override
public void save(SonarLintGlobalSettings newSettings) {
ServerConnectionService.getInstance().setServerConnections(newSettings, connections);
// remove them even if a server with the same name was later added
unbindRemovedServers();
// use background thread because of credentials save
runOnPooledThread(() -> {
ServerConnectionService.getInstance().updateServerConnections(newSettings, new HashSet<>(deletedServerIds), new ArrayList<>(updatedConnectionsByName.values()),
new ArrayList<>(addedConnectionsByName.values()));
// remove them even if a server with the same name was later added
unbindRemovedServers();
});

}

@Override
public void load(SonarLintGlobalSettings settings) {
connections.clear();
updatedConnectionsByName.clear();
addedConnectionsByName.clear();
deletedServerIds.clear();

var listModel = new CollectionListModel<ServerConnection>(new ArrayList<>());
listModel = new CollectionListModel<>(new ArrayList<>());
var serverConnections = ServerConnectionService.getInstance().getConnections();

listModel.add(serverConnections);
connections.addAll(serverConnections);
connectionList.setModel(listModel);

if (!connections.isEmpty()) {
connectionList.setSelectedValue(connections.get(0), true);
if (!serverConnections.isEmpty()) {
connectionList.setSelectedValue(serverConnections.get(0), true);
}
}

Expand All @@ -171,35 +184,64 @@ private ServerConnection getSelectedConnection() {
}

List<ServerConnection> getConnections() {
return connections;
return listModel.getItems();
}

private void editSelectedConnection() {
var selectedConnection = getSelectedConnection();
int selectedIndex = connectionList.getSelectedIndex();

if (selectedConnection != null) {
var serverEditor = ServerConnectionWizard.forConnectionEdition(selectedConnection);
if (serverEditor.showAndGet()) {
var editedConnection = serverEditor.getConnection();
((CollectionListModel<ServerConnection>) connectionList.getModel()).setElementAt(editedConnection, selectedIndex);
connections.set(connections.indexOf(selectedConnection), editedConnection);
connectionChangeListener.changed(connections);
}
var connectionName = selectedConnection.getName();
runOnPooledThread(() -> {
var previousCredentials = getCredentialsForEdition(connectionName);
runOnUiThread(ModalityState.any(), () -> {
var serverEditor = ServerConnectionWizard.forConnectionEdition(new ServerConnectionWithAuth(selectedConnection, previousCredentials));
if (serverEditor.showAndGet()) {
var editedConnectionWithAuth = serverEditor.getConnectionWithAuth();
listModel.setElementAt(editedConnectionWithAuth.getConnection(), selectedIndex);
if (addedConnectionsByName.containsKey(connectionName)) {
addedConnectionsByName.put(connectionName, editedConnectionWithAuth);
} else {
updatedConnectionsByName.put(connectionName, editedConnectionWithAuth);
}
connectionChangeListener.changed(getConnections());
}
});
});
}
}

private ServerConnectionCredentials getCredentialsForEdition(String connectionName) {
var connection = addedConnectionsByName.get(connectionName);
if (connection != null) {
return connection.getCredentials();
}
connection = updatedConnectionsByName.get(connectionName);
if (connection != null) {
return connection.getCredentials();
}
return ServerConnectionService.getInstance().getServerCredentialsByName(connectionName)
.orElseThrow(() -> new IllegalStateException("Credentials for connection '" + connectionName + "' not found"));
}

private class AddConnectionAction implements AnActionButtonRunnable {
@Override
public void run(AnActionButton anActionButton) {
var existingNames = connections.stream().map(ServerConnection::getName).collect(Collectors.toSet());
var existingNames = getConnections().stream().map(ServerConnection::getName).collect(Collectors.toSet());
var wizard = ServerConnectionWizard.forNewConnection(existingNames);
if (wizard.showAndGet()) {
var created = wizard.getConnection();
connections.add(created);
((CollectionListModel<ServerConnection>) connectionList.getModel()).add(created);
connectionList.setSelectedIndex(connectionList.getModel().getSize() - 1);
connectionChangeListener.changed(connections);
var created = wizard.getConnectionWithAuth();
var connectionName = created.getConnection().getName();
if (deletedServerIds.contains(connectionName)) {
updatedConnectionsByName.put(connectionName, created);
deletedServerIds.remove(connectionName);
} else {
addedConnectionsByName.put(connectionName, created);
}
listModel.add(created.getConnection());
connectionList.setSelectedIndex(listModel.getSize() - 1);
connectionChangeListener.changed(getConnections());
}
}
}
Expand Down Expand Up @@ -228,15 +270,18 @@ public void run(AnActionButton anActionButton) {
}
}

var model = (CollectionListModel<ServerConnection>) connectionList.getModel();
// it's not removed from serverIds and editorList
model.remove(connection);
connections.remove(connection);
connectionChangeListener.changed(connections);
listModel.remove(connection);
var connectionName = connection.getName();
if (!addedConnectionsByName.containsKey(connectionName)) {
deletedServerIds.add(connectionName);
}
addedConnectionsByName.remove(connectionName);
updatedConnectionsByName.remove(connectionName);
connectionChangeListener.changed(getConnections());

if (model.getSize() > 0) {
var newIndex = Math.min(model.getSize() - 1, Math.max(selectedIndex - 1, 0));
connectionList.setSelectedValue(model.getElementAt(newIndex), true);
if (listModel.getSize() > 0) {
var newIndex = Math.min(listModel.getSize() - 1, Math.max(selectedIndex - 1, 0));
connectionList.setSelectedValue(listModel.getElementAt(newIndex), true);
}
}

Expand Down
Loading

0 comments on commit 421df9e

Please sign in to comment.