Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,18 @@

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import java.util.function.Consumer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.flywaydb.core.Flyway;
import org.h2.jdbcx.JdbcConnectionPool;
import org.jooq.Configuration;
import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;
import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger;

import static org.sonarsource.sonarlint.core.commons.storage.model.Tables.AI_CODEFIX_SETTINGS;

public final class SonarLintDatabase {
private static final SonarLintLogger LOG = SonarLintLogger.get();

public static final String SQ_IDE_DB_FILENAME = "sq-ide";

private final JdbcConnectionPool dataSource;
Expand Down Expand Up @@ -91,10 +87,6 @@ public DSLContext dsl() {
return dsl;
}

public void withTransaction(Consumer<Configuration> transaction) {
dsl.transaction(transaction::accept);
}

public void shutdown() {
try {
dataSource.dispose();
Expand All @@ -103,10 +95,4 @@ public void shutdown() {
LOG.debug("Error while disposing H2Database: {}", e.getMessage());
}
}

public void cleanupNonExistingConnections(Set<String> existingConnectionIds) {
dsl.deleteFrom(AI_CODEFIX_SETTINGS)
.where(AI_CODEFIX_SETTINGS.CONNECTION_ID.notIn(existingConnectionIds))
.execute();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger;

Expand All @@ -33,31 +32,14 @@ private XodusPurgeUtils() {

private static final SonarLintLogger LOG = SonarLintLogger.get();

public static void purgeOldTemporaryFiles(Path workDir, Integer purgeDays, String pattern) {
if (Files.exists(workDir)) {
try (var stream = Files.newDirectoryStream(workDir, pattern)) {
for (var path : stream) {
var file = path.toFile();
var diff = new Date().getTime() - file.lastModified();
if (diff > purgeDays * 24 * 60 * 60 * 1000) {
FileUtils.deleteQuietly(file);
LOG.debug("Successfully purged " + path);
}
}
} catch (Exception e) {
LOG.error("Unable to purge old temporary files for pattern " + pattern, e);
}
}
}

public static void deleteInFolderWithPattern(Path folder, String pattern) {
if (Files.exists(folder)) {
try (var stream = Files.newDirectoryStream(folder, pattern)) {
for (var path : stream) {
FileUtils.deleteQuietly(path.toFile());
}
} catch (Exception e) {
LOG.error("Unable to remove files in {} for pattern {}", folder, pattern);
LOG.error("Unable to remove files in {} for pattern {}", folder, pattern, e);
}
}
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Flyway computes checksums of migration files and saves them in the DB.

Once a migration has been shipped and applied by some users, it is forbidden to modify a migration file, as the checksum would differ.

This would fail the migration validation at startup, preventing SQ-IDE from starting.

As a rule of thumb, please do not modify a migration file after it has been merged to master, because we cannot know if it has been applied by users.

Instead, please add a new migration file, by respecting the pattern VX__description.sql, and the numbering (+1 compared to the latest migration).
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
package org.sonarsource.sonarlint.core.issue;

import jakarta.annotation.PostConstruct;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
Expand All @@ -44,13 +43,11 @@
import org.sonarsource.sonarlint.core.commons.Transition;
import org.sonarsource.sonarlint.core.commons.Version;
import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger;
import org.sonarsource.sonarlint.core.commons.dogfood.DogfoodEnvironmentDetectionService;
import org.sonarsource.sonarlint.core.commons.progress.SonarLintCancelMonitor;
import org.sonarsource.sonarlint.core.commons.storage.repository.LocalOnlyIssuesRepository;
import org.sonarsource.sonarlint.core.event.LocalOnlyIssueStatusChangedEvent;
import org.sonarsource.sonarlint.core.event.ServerIssueStatusChangedEvent;
import org.sonarsource.sonarlint.core.event.SonarServerEventReceivedEvent;
import org.sonarsource.sonarlint.core.local.only.LocalOnlyIssueStorageService;
import org.sonarsource.sonarlint.core.local.only.XodusLocalOnlyIssueStorageService;
import org.sonarsource.sonarlint.core.mode.SeverityModeService;
import org.sonarsource.sonarlint.core.newcode.NewCodeService;
import org.sonarsource.sonarlint.core.remediation.aicodefix.AiCodeFixService;
Expand All @@ -76,8 +73,8 @@
import org.sonarsource.sonarlint.core.serverapi.proto.sonarqube.ws.Issues;
import org.sonarsource.sonarlint.core.serverapi.push.IssueChangedEvent;
import org.sonarsource.sonarlint.core.serverconnection.ServerInfoSynchronizer;
import org.sonarsource.sonarlint.core.serverconnection.issues.LocalOnlyIssuesRepository;
import org.sonarsource.sonarlint.core.serverconnection.storage.ProjectServerIssueStore;
import org.sonarsource.sonarlint.core.storage.SonarLintDatabaseService;
import org.sonarsource.sonarlint.core.storage.StorageService;
import org.sonarsource.sonarlint.core.tracking.LocalOnlyIssueRepository;
import org.sonarsource.sonarlint.core.tracking.TaintVulnerabilityTrackingService;
Expand Down Expand Up @@ -105,7 +102,7 @@ public class IssueService {
private final ConfigurationRepository configurationRepository;
private final SonarQubeClientManager sonarQubeClientManager;
private final StorageService storageService;
private final LocalOnlyIssueStorageService localOnlyIssueStorageService;
private final XodusLocalOnlyIssueStorageService localOnlyIssueStorageService;
private final LocalOnlyIssueRepository localOnlyIssueRepository;
private final ApplicationEventPublisher eventPublisher;
private final FindingReportingService findingReportingService;
Expand All @@ -114,14 +111,12 @@ public class IssueService {
private final ActiveRulesService activeRulesService;
private final TaintVulnerabilityTrackingService taintVulnerabilityTrackingService;
private final AiCodeFixService aiCodeFixService;
private final DogfoodEnvironmentDetectionService dogfoodEnvironmentDetectionService;
private final SonarLintDatabaseService databaseService;
private final LocalOnlyIssuesRepository localOnlyIssuesRepository;

public IssueService(ConfigurationRepository configurationRepository, SonarQubeClientManager sonarQubeClientManager, StorageService storageService,
LocalOnlyIssueStorageService localOnlyIssueStorageService, LocalOnlyIssueRepository localOnlyIssueRepository,
ApplicationEventPublisher eventPublisher, FindingReportingService findingReportingService, SeverityModeService severityModeService,
NewCodeService newCodeService, ActiveRulesService activeRulesService, TaintVulnerabilityTrackingService taintVulnerabilityTrackingService, AiCodeFixService aiCodeFixService,
DogfoodEnvironmentDetectionService dogfoodEnvironmentDetectionService, SonarLintDatabaseService databaseService) {
XodusLocalOnlyIssueStorageService localOnlyIssueStorageService, LocalOnlyIssueRepository localOnlyIssueRepository, ApplicationEventPublisher eventPublisher,
FindingReportingService findingReportingService, SeverityModeService severityModeService, NewCodeService newCodeService, ActiveRulesService activeRulesService,
TaintVulnerabilityTrackingService taintVulnerabilityTrackingService, AiCodeFixService aiCodeFixService, LocalOnlyIssuesRepository localOnlyIssuesRepository) {
this.configurationRepository = configurationRepository;
this.sonarQubeClientManager = sonarQubeClientManager;
this.storageService = storageService;
Expand All @@ -134,29 +129,25 @@ public IssueService(ConfigurationRepository configurationRepository, SonarQubeCl
this.activeRulesService = activeRulesService;
this.taintVulnerabilityTrackingService = taintVulnerabilityTrackingService;
this.aiCodeFixService = aiCodeFixService;
this.dogfoodEnvironmentDetectionService = dogfoodEnvironmentDetectionService;
this.databaseService = databaseService;
this.localOnlyIssuesRepository = localOnlyIssuesRepository;
}

@PostConstruct
public void migrateData() {
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
if (localOnlyIssueStorageService.exists()) {
try {
LOG.info("Migrating the Xodus local-only issues to H2");
var migrationStart = System.currentTimeMillis();
var repository = new LocalOnlyIssuesRepository(databaseService.getDatabase());
var xodusLocalOnlyIssueStore = localOnlyIssueStorageService.get();
var issuesPerConfigScope = xodusLocalOnlyIssueStore.loadAll();
repository.storeIssues(issuesPerConfigScope);
LOG.info("Migrated Xodus local-only issues to H2, took {}ms", System.currentTimeMillis() - migrationStart);
} catch (Exception e) {
LOG.error("Unable to migrate local-only findings, will use fresh DB", e);
}
if (localOnlyIssueStorageService.exists()) {
try {
LOG.info("Migrating the Xodus local-only issues to H2");
var migrationStart = System.currentTimeMillis();
var xodusLocalOnlyIssueStore = localOnlyIssueStorageService.get();
var issuesPerConfigScope = xodusLocalOnlyIssueStore.loadAll();
localOnlyIssuesRepository.storeIssues(issuesPerConfigScope);
LOG.info("Migrated Xodus local-only issues to H2, took {}ms", System.currentTimeMillis() - migrationStart);
} catch (Exception e) {
LOG.error("Unable to migrate local-only findings, will use fresh DB", e);
}
// always call to remove lingering temporary files
localOnlyIssueStorageService.delete();
}
// always call to remove lingering temporary files
localOnlyIssueStorageService.delete();
}

public void changeStatus(String configurationScopeId, String issueKey, ResolutionStatus newStatus, boolean isTaintIssue, SonarLintCancelMonitor cancelMonitor) {
Expand Down Expand Up @@ -184,10 +175,10 @@ public void changeStatus(String configurationScopeId, String issueKey, Resolutio
var coreStatus = org.sonarsource.sonarlint.core.commons.IssueStatus.valueOf(newStatus.name());
var issue = localIssueOpt.get();
issue.resolve(coreStatus);
var allIssues = loadAllLocalOnlyIssues(configurationScopeId);
var allIssues = localOnlyIssuesRepository.loadAll(configurationScopeId);
serverConnection.withClientApi(serverApi -> serverApi.issue()
.anticipatedTransitions(binding.sonarProjectKey(), concat(allIssues, issue), cancelMonitor));
storeLocalOnlyIssue(configurationScopeId, issue);
localOnlyIssuesRepository.storeLocalOnlyIssue(configurationScopeId, issue);
eventPublisher.publishEvent(new LocalOnlyIssueStatusChangedEvent(issue));
}
}
Expand Down Expand Up @@ -317,40 +308,36 @@ public boolean reopenIssue(String configurationScopeId, String issueId, boolean
public boolean reopenAllIssuesForFile(ReopenAllIssuesForFileParams params, SonarLintCancelMonitor cancelMonitor) {
var configurationScopeId = params.getConfigurationScopeId();
var ideRelativePath = params.getIdeRelativePath();
removeAllIssuesForFile(configurationScopeId, ideRelativePath, cancelMonitor);
return removeAllIssuesForFileFromStore(configurationScopeId, ideRelativePath);
}

private void removeAllIssuesForFile(String configurationScopeId, Path filePath, SonarLintCancelMonitor cancelMonitor) {
var allIssues = loadAllLocalOnlyIssues(configurationScopeId);
var issuesForFile = loadLocalOnlyIssuesForFile(configurationScopeId, filePath);
var allIssues = localOnlyIssuesRepository.loadAll(configurationScopeId);
var issuesForFile = localOnlyIssuesRepository.loadForFile(configurationScopeId, ideRelativePath);
var issuesToSync = subtract(allIssues, issuesForFile);
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
sonarQubeClientManager.getClientOrThrow(binding.connectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.sonarProjectKey(), issuesToSync, cancelMonitor));
return localOnlyIssuesRepository.removeAllIssuesForFile(configurationScopeId, ideRelativePath);
}

private void removeIssueOnServer(String configurationScopeId, UUID issueId, SonarLintCancelMonitor cancelMonitor) {
var allIssues = loadAllLocalOnlyIssues(configurationScopeId);
var allIssues = localOnlyIssuesRepository.loadAll(configurationScopeId);
var issuesToSync = allIssues.stream().filter(it -> !it.getId().equals(issueId)).toList();
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
sonarQubeClientManager.getClientOrThrow(binding.connectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.sonarProjectKey(), issuesToSync, cancelMonitor));
}

private void setCommentOnLocalOnlyIssue(String configurationScopeId, UUID issueId, String comment, SonarLintCancelMonitor cancelMonitor) {
var optionalLocalOnlyIssue = findLocalOnlyIssue(issueId);
var optionalLocalOnlyIssue = localOnlyIssuesRepository.find(issueId);
if (optionalLocalOnlyIssue.isPresent()) {
var commentedIssue = optionalLocalOnlyIssue.get();
var resolution = commentedIssue.getResolution();
if (resolution != null) {
resolution.setComment(comment);
var issuesToSync = new ArrayList<>(loadAllLocalOnlyIssues(configurationScopeId));
var issuesToSync = new ArrayList<>(localOnlyIssuesRepository.loadAll(configurationScopeId));
issuesToSync.replaceAll(issue -> issue.getId().equals(issueId) ? commentedIssue : issue);
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
sonarQubeClientManager.getClientOrThrow(binding.connectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.sonarProjectKey(), issuesToSync, cancelMonitor));
storeLocalOnlyIssue(configurationScopeId, commentedIssue);
localOnlyIssuesRepository.storeLocalOnlyIssue(configurationScopeId, commentedIssue);
}
} else {
throw issueNotFoundException(issueId.toString());
Expand Down Expand Up @@ -383,7 +370,7 @@ private boolean reopenLocalIssue(String issueId, String configurationScopeId, So
}
var issueUuid = issueUuidOptional.get();
removeIssueOnServer(configurationScopeId, issueUuid, cancelMonitor);
return removeLocalOnlyIssue(issueUuid);
return localOnlyIssuesRepository.removeIssue(issueUuid);
}

public EffectiveIssueDetailsDto getEffectiveIssueDetails(String configurationScopeId, UUID findingId, SonarLintCancelMonitor cancelMonitor)
Expand Down Expand Up @@ -561,59 +548,4 @@ private static Optional<UUID> asUUID(String key) {
return Optional.empty();
}
}

// Helper methods to abstract between Xodus and H2 storage
public List<LocalOnlyIssue> loadAllLocalOnlyIssues(String configurationScopeId) {
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
var repository = new LocalOnlyIssuesRepository(databaseService.getDatabase());
return repository.loadAll(configurationScopeId);
} else {
return localOnlyIssueStorageService.get().loadAll(configurationScopeId);
}
}

private List<LocalOnlyIssue> loadLocalOnlyIssuesForFile(String configurationScopeId, Path filePath) {
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
var repository = new LocalOnlyIssuesRepository(databaseService.getDatabase());
return repository.loadForFile(configurationScopeId, filePath);
} else {
return localOnlyIssueStorageService.get().loadForFile(configurationScopeId, filePath);
}
}

private Optional<LocalOnlyIssue> findLocalOnlyIssue(UUID issueId) {
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
var repository = new LocalOnlyIssuesRepository(databaseService.getDatabase());
return repository.find(issueId);
} else {
return localOnlyIssueStorageService.get().find(issueId);
}
}

private void storeLocalOnlyIssue(String configurationScopeId, LocalOnlyIssue issue) {
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
var repository = new LocalOnlyIssuesRepository(databaseService.getDatabase());
repository.storeLocalOnlyIssue(configurationScopeId, issue);
} else {
localOnlyIssueStorageService.get().storeLocalOnlyIssue(configurationScopeId, issue);
}
}

private boolean removeLocalOnlyIssue(UUID issueId) {
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
var repository = new LocalOnlyIssuesRepository(databaseService.getDatabase());
return repository.removeIssue(issueId);
} else {
return localOnlyIssueStorageService.get().removeIssue(issueId);
}
}

private boolean removeAllIssuesForFileFromStore(String configurationScopeId, Path filePath) {
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
var repository = new LocalOnlyIssuesRepository(databaseService.getDatabase());
return repository.removeAllIssuesForFile(configurationScopeId, filePath);
} else {
return localOnlyIssueStorageService.get().removeAllIssuesForFile(configurationScopeId, filePath);
}
}
}
Loading
Loading