aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/MigrateBranchesLiveMeasuresToMeasuresIT.java59
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AbstractMigrateLiveMeasuresToMeasures.java24
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java11
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java200
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java15
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java20
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/SafeModeWebConfigTest.java51
-rw-r--r--server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarQubeIdeConnectionFilterIT.java (renamed from server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarLintConnectionFilterIT.java)32
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java4
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarQubeIdeConnectionFilter.java (renamed from server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarLintConnectionFilter.java)18
10 files changed, 211 insertions, 223 deletions
diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/MigrateBranchesLiveMeasuresToMeasuresIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/MigrateBranchesLiveMeasuresToMeasuresIT.java
index e02264134a6..6418fdeddc4 100644
--- a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/MigrateBranchesLiveMeasuresToMeasuresIT.java
+++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/MigrateBranchesLiveMeasuresToMeasuresIT.java
@@ -22,6 +22,7 @@ package org.sonar.server.platform.db.migration.version.v108;
import com.google.gson.Gson;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -268,6 +269,64 @@ class MigrateBranchesLiveMeasuresToMeasuresIT {
"\"HIGH\":4,\"total\":98723987498723987429874928748748}");
}
+ @Test
+ void should_not_migrate_large_measures() throws SQLException {
+ String nclocMetricUuid = insertMetric("ncloc", "INT");
+ String metricWithDataUuid = insertMetric("metric_with_data", "DATA");
+ String metricWithLargeDataUuid = insertMetric("metric_with_large_data", "DATA");
+
+ String branch1 = "branch_1";
+ insertNotMigratedBranch(branch1);
+ String component1 = uuidFactory.create();
+ insertMeasure(branch1, component1, nclocMetricUuid, Map.of("value", 120));
+ byte[] largeValue = createLargeValue(999_999);
+ insertMeasure(branch1, component1, metricWithDataUuid, Map.of("measure_data", largeValue));
+ insertMeasure(branch1, component1, metricWithLargeDataUuid, Map.of("measure_data", createLargeValue(1_000_000)));
+
+ underTest.execute();
+
+ assertBranchMigrated(branch1);
+ assertThat(db.countRowsOfTable("measures")).isEqualTo(1);
+
+ assertThat(db.select(format(SELECT_MEASURE, component1)))
+ .hasSize(1)
+ .extracting(t -> t.get("component_uuid"), t -> t.get("branch_uuid"),
+ t -> t.get("json_value"), t -> t.get("json_value_hash"))
+ .containsOnly(tuple(component1, branch1, "{\"ncloc\":120.0,\"metric_with_data\":\"" +
+ new String(largeValue, StandardCharsets.UTF_8) + "\"}", -8442559321192521885L));
+ }
+
+ @Test
+ void should_not_migrate_not_persisted_metrics() throws SQLException {
+ String devCostMetricUuid = insertMetric(CoreMetrics.DEVELOPMENT_COST_KEY, "STRING");
+ String nclocDataMetricUuid = insertMetric(CoreMetrics.NCLOC_DATA_KEY, "STRING");
+ String executableLinesDataMetricUuid = insertMetric(CoreMetrics.EXECUTABLE_LINES_DATA_KEY, "STRING");
+
+ String branch1 = "branch_1";
+ insertNotMigratedBranch(branch1);
+ String component1 = uuidFactory.create();
+ insertMeasure(branch1, component1, devCostMetricUuid, Map.of("text_value", "123"));
+ insertMeasure(branch1, component1, nclocDataMetricUuid, Map.of("text_value", "456"));
+ insertMeasure(branch1, component1, executableLinesDataMetricUuid, Map.of("text_value", "789"));
+
+ underTest.execute();
+
+ assertBranchMigrated(branch1);
+ assertThat(db.countRowsOfTable("measures")).isEqualTo(1);
+
+ assertThat(db.select(format(SELECT_MEASURE, component1)))
+ .hasSize(1)
+ .extracting(t -> t.get("component_uuid"), t -> t.get("branch_uuid"),
+ t -> t.get("json_value"), t -> t.get("json_value_hash"))
+ .containsOnly(tuple(component1, branch1, "{\"development_cost\":\"123\"}", -4081454374503046534L));
+ }
+
+ private byte[] createLargeValue(int size) {
+ byte[] value = new byte[size];
+ Arrays.fill(value, (byte) 'a');
+ return value;
+ }
+
private void assertBranchMigrated(String branch) {
List<Map<String, Object>> result = db.select(format("select %s as \"MIGRATED\" from project_branches where uuid = '%s'", MEASURES_MIGRATED_COLUMN, branch));
assertThat(result)
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AbstractMigrateLiveMeasuresToMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AbstractMigrateLiveMeasuresToMeasures.java
index ef338b918e2..e1cac143632 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AbstractMigrateLiveMeasuresToMeasures.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AbstractMigrateLiveMeasuresToMeasures.java
@@ -34,6 +34,10 @@ import org.slf4j.LoggerFactory;
import org.sonar.api.utils.System2;
import org.sonar.db.Database;
import org.sonar.db.DatabaseUtils;
+import org.sonar.db.dialect.H2;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.step.MassUpdate;
import org.sonar.server.platform.db.migration.step.Select;
@@ -58,6 +62,8 @@ public abstract class AbstractMigrateLiveMeasuresToMeasures extends DataChange {
FROM live_measures lm
INNER JOIN metrics m ON m.uuid = lm.metric_uuid
WHERE lm.project_uuid = ?
+ AND (lm.measure_data is null OR %s(lm.measure_data) < 1000000)
+ AND m.name NOT IN ('executable_lines_data', 'ncloc_data')
ORDER BY lm.component_uuid
""";
@@ -119,9 +125,11 @@ public abstract class AbstractMigrateLiveMeasuresToMeasures extends DataChange {
LOGGER.info("Starting the migration of {} {}s (total number of {}s: {})", uuids.size(), item, item, total);
int migrated = 0;
+ String selectQuery = String.format(SELECT_QUERY, getByteLengthFunction());
+
for (String uuid : uuids) {
try {
- migrateItem(uuid, context);
+ migrateItem(uuid, context, selectQuery);
} catch (Exception e) {
LOGGER.error(format("Migration of %s %s failed", item, uuid));
throw e;
@@ -134,14 +142,24 @@ public abstract class AbstractMigrateLiveMeasuresToMeasures extends DataChange {
}
}
- private void migrateItem(String uuid, Context context) throws SQLException {
+ private String getByteLengthFunction() {
+ return switch (getDialect().getId()) {
+ case PostgreSql.ID -> "OCTET_LENGTH";
+ case MsSql.ID -> "DATALENGTH";
+ case Oracle.ID -> "DBMS_LOB.GETLENGTH";
+ case H2.ID -> "LENGTH";
+ default -> throw new IllegalStateException("Unsupported dialect: " + getDialect().getId());
+ };
+ }
+
+ private void migrateItem(String uuid, Context context, String selectQuery) throws SQLException {
LOGGER.debug("Migrating {} {}...", item, uuid);
Map<String, Object> measureValues = new HashMap<>();
AtomicReference<String> componentUuid = new AtomicReference<>(null);
MassUpdate massUpdate = context.prepareMassUpdate();
- massUpdate.select(SELECT_QUERY).setString(1, uuid);
+ massUpdate.select(selectQuery).setString(1, uuid);
massUpdate.update(INSERT_QUERY);
massUpdate.execute((row, update) -> {
boolean shouldUpdate = false;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java
index 8d45e96db24..669296482a4 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.v2.api.system.controller;
+import javax.annotation.Nullable;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.health.Health;
@@ -26,6 +27,7 @@ import org.sonar.server.health.HealthChecker;
import org.sonar.server.platform.NodeInformation;
import org.sonar.server.user.SystemPasscode;
import org.sonar.server.user.UserSession;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -47,18 +49,15 @@ public class HealthController {
private final NodeInformation nodeInformation;
private final UserSession userSession;
- public HealthController(HealthChecker healthChecker, SystemPasscode systemPasscode, NodeInformation nodeInformation,
- UserSession userSession) {
+ @Autowired(required=true)
+ public HealthController(HealthChecker healthChecker, SystemPasscode systemPasscode, @Nullable NodeInformation nodeInformation,
+ @Nullable UserSession userSession) {
this.healthChecker = healthChecker;
this.systemPasscode = systemPasscode;
this.nodeInformation = nodeInformation;
this.userSession = userSession;
}
- public HealthController(HealthChecker healthChecker, SystemPasscode systemPasscode) {
- this(healthChecker, systemPasscode, null, null);
- }
-
@GetMapping
public Health getHealth(@RequestHeader(value = "X-Sonar-Passcode", required = false) String requestPassCode) {
if (systemPasscode.isValidPasscode(requestPassCode) || isSystemAdmin()) {
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
index bd010589ae6..962682037d7 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
@@ -19,75 +19,30 @@
*/
package org.sonar.server.v2.config;
-import org.sonar.api.platform.Server;
-import org.sonar.api.resources.Languages;
-import org.sonar.db.Database;
-import org.sonar.db.DbClient;
-import org.sonar.server.common.email.config.EmailConfigurationService;
-import org.sonar.server.common.github.config.GithubConfigurationService;
-import org.sonar.server.common.gitlab.config.GitlabConfigurationService;
-import org.sonar.server.common.group.service.GroupMembershipService;
-import org.sonar.server.common.group.service.GroupService;
-import org.sonar.server.common.management.ManagedInstanceChecker;
-import org.sonar.server.common.platform.LivenessChecker;
-import org.sonar.server.common.project.ImportProjectService;
-import org.sonar.server.common.projectbindings.service.ProjectBindingsService;
-import org.sonar.server.common.rule.service.RuleService;
-import org.sonar.server.common.text.MacroInterpreter;
-import org.sonar.server.common.user.service.UserService;
-import org.sonar.server.health.HealthChecker;
-import org.sonar.server.notification.NotificationManager;
-import org.sonar.server.platform.NodeInformation;
-import org.sonar.server.platform.ServerFileSystem;
-import org.sonar.server.platform.db.migration.DatabaseMigrationState;
-import org.sonar.server.platform.db.migration.version.DatabaseVersion;
-import org.sonar.server.qualitygate.QualityGateConditionsValidator;
import org.sonar.server.rule.ActiveRuleService;
-import org.sonar.server.rule.RuleDescriptionFormatter;
-import org.sonar.server.setting.SettingsChangeNotifier;
-import org.sonar.server.user.SystemPasscode;
import org.sonar.server.user.UserSession;
-import org.sonar.server.v2.api.analysis.controller.ActiveRulesController;
import org.sonar.server.v2.api.analysis.controller.DefaultActiveRulesController;
import org.sonar.server.v2.api.analysis.controller.DefaultJresController;
import org.sonar.server.v2.api.analysis.controller.DefaultScannerEngineController;
import org.sonar.server.v2.api.analysis.controller.DefaultVersionController;
-import org.sonar.server.v2.api.analysis.controller.JresController;
-import org.sonar.server.v2.api.analysis.controller.ScannerEngineController;
-import org.sonar.server.v2.api.analysis.controller.VersionController;
-import org.sonar.server.v2.api.analysis.service.ActiveRulesHandler;
import org.sonar.server.v2.api.analysis.service.ActiveRulesHandlerImpl;
-import org.sonar.server.v2.api.analysis.service.JresHandler;
import org.sonar.server.v2.api.analysis.service.JresHandlerImpl;
-import org.sonar.server.v2.api.analysis.service.ScannerEngineHandler;
import org.sonar.server.v2.api.analysis.service.ScannerEngineHandlerImpl;
import org.sonar.server.v2.api.dop.controller.DefaultDopSettingsController;
-import org.sonar.server.v2.api.dop.controller.DopSettingsController;
import org.sonar.server.v2.api.email.config.controller.DefaultEmailConfigurationController;
-import org.sonar.server.v2.api.email.config.controller.EmailConfigurationController;
import org.sonar.server.v2.api.github.config.controller.DefaultGithubConfigurationController;
-import org.sonar.server.v2.api.github.config.controller.GithubConfigurationController;
import org.sonar.server.v2.api.gitlab.config.controller.DefaultGitlabConfigurationController;
-import org.sonar.server.v2.api.gitlab.config.controller.GitlabConfigurationController;
import org.sonar.server.v2.api.group.controller.DefaultGroupController;
-import org.sonar.server.v2.api.group.controller.GroupController;
import org.sonar.server.v2.api.membership.controller.DefaultGroupMembershipController;
-import org.sonar.server.v2.api.membership.controller.GroupMembershipController;
import org.sonar.server.v2.api.mode.controller.DefaultModeController;
-import org.sonar.server.v2.api.mode.controller.ModeController;
import org.sonar.server.v2.api.projectbindings.controller.DefaultProjectBindingsController;
-import org.sonar.server.v2.api.projectbindings.controller.ProjectBindingsController;
-import org.sonar.server.v2.api.projects.controller.BoundProjectsController;
import org.sonar.server.v2.api.projects.controller.DefaultBoundProjectsController;
import org.sonar.server.v2.api.rule.controller.DefaultRuleController;
-import org.sonar.server.v2.api.rule.controller.RuleController;
import org.sonar.server.v2.api.rule.converter.RuleRestResponseGenerator;
import org.sonar.server.v2.api.system.controller.DatabaseMigrationsController;
import org.sonar.server.v2.api.system.controller.DefaultLivenessController;
import org.sonar.server.v2.api.system.controller.HealthController;
-import org.sonar.server.v2.api.system.controller.LivenessController;
import org.sonar.server.v2.api.user.controller.DefaultUserController;
-import org.sonar.server.v2.api.user.controller.UserController;
import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator;
import org.sonar.server.v2.common.DeprecatedHandler;
import org.springframework.context.annotation.Bean;
@@ -97,60 +52,35 @@ import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configuration
-@Import(CommonWebConfig.class)
+@Import({
+ ActiveRulesHandlerImpl.class,
+ ActiveRuleService.class,
+ CommonWebConfig.class,
+ DatabaseMigrationsController.class,
+ DefaultActiveRulesController.class,
+ DefaultBoundProjectsController.class,
+ DefaultDopSettingsController.class,
+ DefaultEmailConfigurationController.class,
+ DefaultGithubConfigurationController.class,
+ DefaultGitlabConfigurationController.class,
+ DefaultGroupController.class,
+ DefaultGroupMembershipController.class,
+ DefaultJresController.class,
+ DefaultLivenessController.class,
+ DefaultModeController.class,
+ DefaultProjectBindingsController.class,
+ DefaultRuleController.class,
+ DefaultScannerEngineController.class,
+ DefaultUserController.class,
+ DefaultVersionController.class,
+ HealthController.class,
+ JresHandlerImpl.class,
+ ScannerEngineHandlerImpl.class,
+ UsersSearchRestResponseGenerator.class,
+ RuleRestResponseGenerator.class
+})
public class PlatformLevel4WebConfig {
- @Bean
- public LivenessController livenessController(LivenessChecker livenessChecker, UserSession userSession, SystemPasscode systemPasscode) {
- return new DefaultLivenessController(livenessChecker, systemPasscode, userSession);
- }
-
- @Bean
- public HealthController healthController(HealthChecker healthChecker, SystemPasscode systemPasscode, NodeInformation nodeInformation,
- UserSession userSession) {
- return new HealthController(healthChecker, systemPasscode, nodeInformation, userSession);
- }
-
- @Bean
- public DatabaseMigrationsController databaseMigrationsController(DatabaseVersion databaseVersion, DatabaseMigrationState databaseMigrationState,
- Database database) {
- return new DatabaseMigrationsController(databaseVersion, databaseMigrationState, database);
- }
-
- @Bean
- public UsersSearchRestResponseGenerator usersSearchResponseGenerator(UserSession userSession) {
- return new UsersSearchRestResponseGenerator(userSession);
- }
-
- @Bean
- public UserController userController(
- UserSession userSession,
- UsersSearchRestResponseGenerator usersSearchResponseGenerator,
- UserService userService) {
- return new DefaultUserController(userSession, userService, usersSearchResponseGenerator);
- }
-
- @Bean
- public GroupController groupController(UserSession userSession, DbClient dbClient, GroupService groupService, ManagedInstanceChecker managedInstanceChecker) {
- return new DefaultGroupController(userSession, dbClient, groupService, managedInstanceChecker);
- }
-
- @Bean
- public GroupMembershipController groupMembershipsController(UserSession userSession,
- GroupMembershipService groupMembershipService, ManagedInstanceChecker managedInstanceChecker) {
- return new DefaultGroupMembershipController(userSession, groupMembershipService, managedInstanceChecker);
- }
-
- @Bean
- public RuleRestResponseGenerator ruleRestResponseGenerator(Languages languages, MacroInterpreter macroInterpreter, RuleDescriptionFormatter ruleDescriptionFormatter) {
- return new RuleRestResponseGenerator(languages, macroInterpreter, ruleDescriptionFormatter);
- }
-
- @Bean
- public RuleController ruleController(UserSession userSession, RuleService ruleService, RuleRestResponseGenerator ruleRestResponseGenerator) {
- return new DefaultRuleController(userSession, ruleService, ruleRestResponseGenerator);
- }
-
@Primary
@Bean("org.sonar.server.v2.config.PlatformLevel4WebConfig.requestMappingHandlerMapping")
public RequestMappingHandlerMapping requestMappingHandlerMapping(UserSession userSession) {
@@ -159,80 +89,4 @@ public class PlatformLevel4WebConfig {
return handlerMapping;
}
- @Bean
- public GitlabConfigurationController gitlabConfigurationController(UserSession userSession, GitlabConfigurationService gitlabConfigurationService) {
- return new DefaultGitlabConfigurationController(userSession, gitlabConfigurationService);
- }
-
- @Bean
- public GithubConfigurationController githubConfigurationController(UserSession userSession, GithubConfigurationService githubConfigurationService) {
- return new DefaultGithubConfigurationController(userSession, githubConfigurationService);
- }
-
- @Bean
- public BoundProjectsController importedProjectsController(UserSession userSession, ImportProjectService importProjectService) {
- return new DefaultBoundProjectsController(userSession, importProjectService);
- }
-
- @Bean
- public DopSettingsController dopSettingsController(UserSession userSession, DbClient dbClient) {
- return new DefaultDopSettingsController(userSession, dbClient);
- }
-
- @Bean
- public ProjectBindingsController projectBindingsController(UserSession userSession, ProjectBindingsService projectBindingsService) {
- return new DefaultProjectBindingsController(userSession, projectBindingsService);
- }
-
- @Bean
- public VersionController versionController(Server server) {
- return new DefaultVersionController(server);
- }
-
- @Bean
- public JresHandler jresHandler() {
- return new JresHandlerImpl();
- }
-
- @Bean
- public JresController jresController(JresHandler jresHandler) {
- return new DefaultJresController(jresHandler);
- }
-
- @Bean
- public ScannerEngineHandler scannerEngineHandler(ServerFileSystem serverFileSystem) {
- return new ScannerEngineHandlerImpl(serverFileSystem);
- }
-
- @Bean
- public ScannerEngineController scannerEngineController(ScannerEngineHandler scannerEngineHandler) {
- return new DefaultScannerEngineController(scannerEngineHandler);
- }
-
- @Bean
- public EmailConfigurationController emailConfigurationController(UserSession userSession, EmailConfigurationService emailConfigurationService) {
- return new DefaultEmailConfigurationController(userSession, emailConfigurationService);
- }
-
- @Bean
- public ModeController modeController(UserSession userSession, org.sonar.api.config.Configuration configuration, DbClient dbClient,
- SettingsChangeNotifier settingsChangeNotifier, NotificationManager notificationManager, QualityGateConditionsValidator qualityGateConditionsValidator) {
- return new DefaultModeController(userSession, dbClient, configuration, settingsChangeNotifier, notificationManager, qualityGateConditionsValidator);
- }
-
- @Bean
- public ActiveRuleService activeRuleService(DbClient dbClient, Languages languages) {
- return new ActiveRuleService(dbClient, languages);
- }
-
- @Bean
- public ActiveRulesHandler activeRulesHandler(DbClient dbClient, ActiveRuleService activeRuleService) {
- return new ActiveRulesHandlerImpl(dbClient, activeRuleService);
- }
-
- @Bean
- public ActiveRulesController activeRulesController(ActiveRulesHandler activeRulesHandler) {
- return new DefaultActiveRulesController(activeRulesHandler);
- }
-
}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java
index 1810e0dbbc9..db4cf56e25f 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java
@@ -19,11 +19,8 @@
*/
package org.sonar.server.v2.config;
-import org.sonar.db.Database;
import org.sonar.server.common.platform.LivenessChecker;
import org.sonar.server.health.HealthChecker;
-import org.sonar.server.platform.db.migration.DatabaseMigrationState;
-import org.sonar.server.platform.db.migration.version.DatabaseVersion;
import org.sonar.server.user.SystemPasscode;
import org.sonar.server.v2.api.system.controller.DatabaseMigrationsController;
import org.sonar.server.v2.api.system.controller.DefaultLivenessController;
@@ -36,7 +33,10 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@EnableWebMvc
-@Import(CommonWebConfig.class)
+@Import({
+ CommonWebConfig.class,
+ DatabaseMigrationsController.class
+})
public class SafeModeWebConfig {
@Bean
@@ -46,12 +46,7 @@ public class SafeModeWebConfig {
@Bean
public HealthController healthController(HealthChecker healthChecker, SystemPasscode systemPasscode) {
- return new HealthController(healthChecker, systemPasscode);
+ return new HealthController(healthChecker, systemPasscode, null, null);
}
- @Bean
- public DatabaseMigrationsController databaseMigrationsController(DatabaseVersion databaseVersion, DatabaseMigrationState databaseMigrationState,
- Database database) {
- return new DatabaseMigrationsController(databaseVersion, databaseMigrationState, database);
- }
}
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java
index 27c05bc9e3f..dc2e42ad426 100644
--- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java
@@ -23,15 +23,8 @@ import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import org.sonar.api.platform.Server;
-import org.sonar.server.platform.ServerFileSystem;
-import org.sonar.server.v2.api.analysis.controller.DefaultJresController;
-import org.sonar.server.v2.api.analysis.controller.DefaultVersionController;
-import org.sonar.server.v2.api.analysis.controller.DefaultScannerEngineController;
-import org.sonar.server.v2.api.analysis.service.JresHandler;
-import org.sonar.server.v2.api.analysis.service.JresHandlerImpl;
-import org.sonar.server.v2.api.analysis.service.ScannerEngineHandler;
-import org.sonar.server.v2.api.analysis.service.ScannerEngineHandlerImpl;
+import org.sonar.server.user.UserSession;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
@@ -43,17 +36,12 @@ class PlatformLevel4WebConfigTest {
private static Stream<Arguments> components() {
return Stream.of(
- arguments(platformLevel4WebConfig.versionController(mock(Server.class)), DefaultVersionController.class),
- arguments(platformLevel4WebConfig.jresHandler(), JresHandlerImpl.class),
- arguments(platformLevel4WebConfig.jresController(mock(JresHandler.class)), DefaultJresController.class),
- arguments(platformLevel4WebConfig.scannerEngineHandler(mock(ServerFileSystem.class)), ScannerEngineHandlerImpl.class),
- arguments(platformLevel4WebConfig.scannerEngineController(mock(ScannerEngineHandler.class)), DefaultScannerEngineController.class)
- );
+ arguments(platformLevel4WebConfig.requestMappingHandlerMapping(mock(UserSession.class)), RequestMappingHandlerMapping.class));
}
@ParameterizedTest
@MethodSource("components")
- void components_shouldBeInjectedInPlatformLevel4WebConfig(Object component, Class<?> instanceClass) {
+ void custom_components_shouldBeInjectedInPlatformLevel4WebConfig(Object component, Class<?> instanceClass) {
assertThat(component).isNotNull().isInstanceOf(instanceClass);
}
}
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/SafeModeWebConfigTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/SafeModeWebConfigTest.java
new file mode 100644
index 00000000000..a9d87095b5a
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/SafeModeWebConfigTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2025 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.v2.config;
+
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.sonar.server.common.platform.LivenessChecker;
+import org.sonar.server.health.HealthChecker;
+import org.sonar.server.user.SystemPasscode;
+import org.sonar.server.v2.api.system.controller.DefaultLivenessController;
+import org.sonar.server.v2.api.system.controller.HealthController;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+import static org.mockito.Mockito.mock;
+
+class SafeModeWebConfigTest {
+
+ private static final SafeModeWebConfig safeModeWebConfig = new SafeModeWebConfig();
+
+ private static Stream<Arguments> components() {
+ return Stream.of(
+ arguments(safeModeWebConfig.livenessController(mock(LivenessChecker.class), mock(SystemPasscode.class)), DefaultLivenessController.class),
+ arguments(safeModeWebConfig.healthController(mock(HealthChecker.class), mock(SystemPasscode.class)), HealthController.class));
+ }
+
+ @ParameterizedTest
+ @MethodSource("components")
+ void custom_components_shouldBeInjectedInSafeModeWebConfig(Object component, Class<?> instanceClass) {
+ assertThat(component).isNotNull().isInstanceOf(instanceClass);
+ }
+}
diff --git a/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarLintConnectionFilterIT.java b/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarQubeIdeConnectionFilterIT.java
index 9ae4f71f988..302eff253ab 100644
--- a/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarLintConnectionFilterIT.java
+++ b/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarQubeIdeConnectionFilterIT.java
@@ -40,7 +40,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-public class SonarLintConnectionFilterIT {
+public class SonarQubeIdeConnectionFilterIT {
private static final String LOGIN = "user1";
private final TestSystem2 system2 = new TestSystem2();
@@ -52,6 +52,24 @@ public class SonarLintConnectionFilterIT {
system2.setNow(10_000_000L);
addUser(LOGIN, 1_000_000L);
+ runFilter(LOGIN, "SonarQube for IDE");
+ assertThat(getLastUpdate(LOGIN)).isEqualTo(10_000_000L);
+ }
+
+ @Test
+ public void update_with_rebranding_transitional_agent() throws IOException {
+ system2.setNow(10_000_000L);
+ addUser(LOGIN, 1_000_000L);
+
+ runFilter(LOGIN, "SonarQube for IDE (SonarLint)");
+ assertThat(getLastUpdate(LOGIN)).isEqualTo(10_000_000L);
+ }
+
+ @Test
+ public void update_with_legacy_sonarlint_agent() throws IOException {
+ system2.setNow(10_000_000L);
+ addUser(LOGIN, 1_000_000L);
+
runFilter(LOGIN, "SonarLint for IntelliJ");
assertThat(getLastUpdate(LOGIN)).isEqualTo(10_000_000L);
}
@@ -67,7 +85,7 @@ public class SonarLintConnectionFilterIT {
@Test
public void only_applies_to_api() {
- SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), mock(ThreadLocalUserSession.class), system2);
+ SonarQubeIdeConnectionFilter underTest = new SonarQubeIdeConnectionFilter(dbTester.getDbClient(), mock(ThreadLocalUserSession.class), system2);
assertThat(underTest.doGetPattern().matches("/api/test")).isTrue();
assertThat(underTest.doGetPattern().matches("/test")).isFalse();
@@ -94,10 +112,12 @@ public class SonarLintConnectionFilterIT {
@Test
public void dont_fail_if_no_user_set() throws IOException {
- SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), new ThreadLocalUserSession(), system2);
- HttpServletRequest httpRequest = mock(HttpServletRequest.class);
+ var userSession = new ThreadLocalUserSession();
+ userSession.unload();
+ var underTest = new SonarQubeIdeConnectionFilter(dbTester.getDbClient(), userSession, system2);
+ var httpRequest = mock(HttpServletRequest.class);
when(httpRequest.getHeader("User-Agent")).thenReturn("sonarlint");
- FilterChain chain = mock(FilterChain.class);
+ var chain = mock(FilterChain.class);
underTest.doFilter(new JakartaHttpRequest(httpRequest), mock(HttpResponse.class), chain);
verify(chain).doFilter(any(), any());
}
@@ -124,7 +144,7 @@ public class SonarLintConnectionFilterIT {
UserDto user = dbTester.getDbClient().userDao().selectByLogin(dbTester.getSession(), loggedInUser);
ThreadLocalUserSession session = new ThreadLocalUserSession();
session.set(new ServerUserSession(dbTester.getDbClient(), user, false));
- SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), session, system2);
+ SonarQubeIdeConnectionFilter underTest = new SonarQubeIdeConnectionFilter(dbTester.getDbClient(), session, system2);
HttpServletRequest httpRequest = mock(HttpServletRequest.class);
when(httpRequest.getHeader("User-Agent")).thenReturn(agent);
FilterChain chain = mock(FilterChain.class);
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index a9c53d053e9..6e677fcfd0b 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -206,7 +206,7 @@ import org.sonar.server.platform.telemetry.TelemetrySubportfolioSelectionModePro
import org.sonar.server.platform.telemetry.TelemetryUserEnabledProvider;
import org.sonar.server.platform.telemetry.TelemetryVersionProvider;
import org.sonar.server.platform.web.ActionDeprecationLoggerInterceptor;
-import org.sonar.server.platform.web.SonarLintConnectionFilter;
+import org.sonar.server.platform.web.SonarQubeIdeConnectionFilter;
import org.sonar.server.platform.web.WebServiceFilter;
import org.sonar.server.platform.web.WebServiceReroutingFilter;
import org.sonar.server.platform.web.requestid.HttpRequestIdModule;
@@ -422,7 +422,7 @@ public class PlatformLevel4 extends PlatformLevel {
ActionDeprecationLoggerInterceptor.class,
WebServiceEngine.class,
new WebServicesWsModule(),
- SonarLintConnectionFilter.class,
+ SonarQubeIdeConnectionFilter.class,
WebServiceFilter.class,
WebServiceReroutingFilter.class,
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarLintConnectionFilter.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarQubeIdeConnectionFilter.java
index 27c6b6fa117..d77c3f650fd 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarLintConnectionFilter.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarQubeIdeConnectionFilter.java
@@ -21,7 +21,6 @@ package org.sonar.server.platform.web;
import java.io.IOException;
import java.util.Locale;
-import java.util.Optional;
import org.sonar.api.server.http.HttpRequest;
import org.sonar.api.server.http.HttpResponse;
import org.sonar.api.utils.System2;
@@ -35,7 +34,7 @@ import org.sonar.server.ws.ServletRequest;
import static java.util.concurrent.TimeUnit.HOURS;
-public class SonarLintConnectionFilter extends HttpFilter {
+public class SonarQubeIdeConnectionFilter extends HttpFilter {
private static final UrlPattern URL_PATTERN = UrlPattern.builder()
.includes("/api/*")
.excludes("/api/v2/*")
@@ -44,7 +43,7 @@ public class SonarLintConnectionFilter extends HttpFilter {
private final ThreadLocalUserSession userSession;
private final System2 system2;
- public SonarLintConnectionFilter(DbClient dbClient, ThreadLocalUserSession userSession, System2 system2) {
+ public SonarQubeIdeConnectionFilter(DbClient dbClient, ThreadLocalUserSession userSession, System2 system2) {
this.dbClient = dbClient;
this.userSession = userSession;
this.system2 = system2;
@@ -57,15 +56,20 @@ public class SonarLintConnectionFilter extends HttpFilter {
@Override
public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException {
- ServletRequest wsRequest = new ServletRequest(request);
-
- Optional<String> agent = wsRequest.header("User-Agent");
- if (agent.isPresent() && agent.get().toLowerCase(Locale.ENGLISH).contains("sonarlint")) {
+ if (isComingFromSonarQubeIde(request)) {
update();
}
chain.doFilter(request, response);
}
+ private static boolean isComingFromSonarQubeIde(HttpRequest request) {
+ ServletRequest wsRequest = new ServletRequest(request);
+ return wsRequest.header("User-Agent")
+ .map(agent -> agent.toLowerCase(Locale.ENGLISH))
+ .filter(agent -> agent.contains("sonarlint") || agent.contains("sonarqube for ide"))
+ .isPresent();
+ }
+
public void update() {
if (shouldUpdate()) {
try (DbSession session = dbClient.openSession(false)) {