aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStep.java20
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStepTest.java38
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisWarningsStepTest.java13
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessages.java15
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessagesImpl.java10
-rw-r--r--server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java1
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java7
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java9
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java24
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java4
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageType.java35
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java7
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java1
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessageDto.java92
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesDao.java63
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesMapper.java37
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml25
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml4
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserDismissedMessagesMapper.xml60
-rw-r--r--server/sonar-db-dao/src/schema/schema-sq.ddl15
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java1
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java54
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java17
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java32
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDismissedMessagesDaoTest.java152
-rw-r--r--server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java13
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTable.java42
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTable.java (renamed from server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddIsDismissibleColumnToCeTaskMessageTable.java)16
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTable.java102
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/DbVersion85.java10
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTable.java (renamed from server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/MakeIsDismissibleColumnNotNullableOnCeTaskMessageTable.java)21
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTable.java (renamed from server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateIsDismissibleColumnOfCeTaskMessageTable.java)14
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest.java (renamed from server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddIsDismissibleColumnToCeTaskMessageTableTest.java)16
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest.java63
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTableTest.java55
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest.java (renamed from server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/MakeIsDismissibleColumnNotNullableOnCeTaskMessageTableTest.java)13
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateIsDismissibleColumnOfCeTaskMessageTableTest.java87
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest.java73
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest/schema.sql9
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest/schema.sql9
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest/schema.sql9
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest/schema.sql9
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/startup/UpgradeSuggestionsCleaner.java70
-rw-r--r--server/sonar-webserver-core/src/test/java/org/sonar/server/startup/UpgradeSuggestionsCleanerTest.java148
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/AnalysisStatusAction.java2
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/CeWsModule.java3
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/DismissAnalysisWarningAction.java115
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DeactivateAction.java5
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java2
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/AnalysisStatusActionTest.java11
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java2
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java2
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/DismissAnalysisWarningActionTest.java208
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/TaskActionTest.java11
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java40
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java4
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java16
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/ce/DismissAnalysisWarningRequest.java59
61 files changed, 1775 insertions, 226 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStep.java
index d5c78acab54..bd29a24839f 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStep.java
@@ -36,6 +36,7 @@ import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
import org.sonar.ce.task.step.ComputationStep;
import org.sonar.core.platform.EditionProvider;
import org.sonar.core.platform.PlatformEditionProvider;
+import org.sonar.db.ce.CeTaskMessageType;
import static java.lang.String.format;
import static org.sonar.core.language.UnanalyzedLanguages.C;
@@ -50,9 +51,9 @@ public class HandleUnanalyzedLanguagesStep implements ComputationStep {
static final String DESCRIPTION = "Check upgrade possibility for not analyzed code files.";
- private static final String LANGUAGE_UPGRADE_MESSAGE = "%s file(s) detected during the last analysis. %s code cannot be analyzed with SonarQube " +
- "community edition. Please consider <a href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to " +
- "the Developer Edition</a> to analyze this language.";
+ private static final String LANGUAGE_UPGRADE_MESSAGE = "%s detected in this project during the last analysis. %s cannot be analyzed with your" +
+ " current SonarQube edition. Please consider <a target=\"_blank\" href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to" +
+ " Developer Edition</a> to find Bugs, Code Smells, Vulnerabilities and Security Hotspots in %s.";
private final BatchReportReader reportReader;
private final CeTaskMessages ceTaskMessages;
@@ -102,7 +103,7 @@ public class HandleUnanalyzedLanguagesStep implements ComputationStep {
Iterator<Map.Entry<String, Integer>> iterator = sortedLanguageMap.entrySet().iterator();
Map.Entry<String, Integer> firstLanguage = iterator.next();
StringBuilder languageLabel = new StringBuilder(firstLanguage.getKey());
- StringBuilder fileCountLabel = new StringBuilder(format("%s %s", firstLanguage.getValue(), firstLanguage.getKey()));
+ StringBuilder fileCountLabel = new StringBuilder(format("%s unanalyzed %s", firstLanguage.getValue(), firstLanguage.getKey()));
while (iterator.hasNext()) {
Map.Entry<String, Integer> nextLanguage = iterator.next();
if (iterator.hasNext()) {
@@ -113,10 +114,17 @@ public class HandleUnanalyzedLanguagesStep implements ComputationStep {
fileCountLabel.append(" and ");
}
languageLabel.append(nextLanguage.getKey());
- fileCountLabel.append(format("%s %s", nextLanguage.getValue(), nextLanguage.getKey()));
+ fileCountLabel.append(format("%s unanalyzed %s", nextLanguage.getValue(), nextLanguage.getKey()));
}
- return new CeTaskMessages.Message(format(LANGUAGE_UPGRADE_MESSAGE, fileCountLabel, languageLabel), system.now(), true);
+ if (sortedLanguageMap.size() == 1 && sortedLanguageMap.entrySet().iterator().next().getValue() == 1) {
+ fileCountLabel.append(" file was");
+ } else {
+ fileCountLabel.append(" files were");
+ }
+
+ String message = format(LANGUAGE_UPGRADE_MESSAGE, fileCountLabel, languageLabel, sortedLanguageMap.size() == 1 ? "this file" : "these files");
+ return new CeTaskMessages.Message(message, system.now(), CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
}
private void computeMeasures(Map<String, Integer> filesPerLanguage) {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStepTest.java
index 6aba65c5494..bc0726aac4b 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/language/HandleUnanalyzedLanguagesStepTest.java
@@ -24,7 +24,10 @@ import java.util.List;
import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.junit.MockitoJUnitRunner;
import org.sonar.api.utils.System2;
import org.sonar.ce.task.log.CeTaskMessages;
import org.sonar.ce.task.log.CeTaskMessages.Message;
@@ -37,6 +40,7 @@ import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
import org.sonar.ce.task.step.TestComputationStepContext;
import org.sonar.core.platform.EditionProvider;
import org.sonar.core.platform.PlatformEditionProvider;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.scanner.protocol.output.ScannerReport;
import static com.google.common.collect.ImmutableList.of;
@@ -54,6 +58,7 @@ import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP;
import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY;
import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_C_KEY;
+@RunWith(MockitoJUnitRunner.class)
public class HandleUnanalyzedLanguagesStepTest {
private static final int PROJECT_REF = 1;
@@ -70,6 +75,9 @@ public class HandleUnanalyzedLanguagesStepTest {
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+ @Captor
+ private ArgumentCaptor<Message> argumentCaptor;
+
private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
private final CeTaskMessages ceTaskMessages = mock(CeTaskMessages.class);
@@ -93,18 +101,18 @@ public class HandleUnanalyzedLanguagesStepTest {
.putNotAnalyzedFilesByLanguage("C", 10)
.putNotAnalyzedFilesByLanguage("SomeLang", 1000)
.build());
- ArgumentCaptor<Message> argumentCaptor = ArgumentCaptor.forClass(Message.class);
underTest.execute(new TestComputationStepContext());
verify(ceTaskMessages, times(1)).add(argumentCaptor.capture());
assertThat(argumentCaptor.getAllValues())
- .extracting(Message::getText, Message::isDismissible)
+ .extracting(Message::getText, Message::getType)
.containsExactly(tuple(
- "10 C, 20 C++ and 1000 SomeLang file(s) detected during the last analysis. C, C++ and SomeLang code cannot be analyzed with SonarQube community " +
- "edition. Please consider <a href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to the Developer " +
- "Edition</a> to analyze this language.",
- true));
+ "10 unanalyzed C, 20 unanalyzed C++ and 1000 unanalyzed SomeLang files were detected in this project during the last analysis. C," +
+ " C++ and SomeLang cannot be analyzed with your current SonarQube edition. Please consider" +
+ " <a target=\"_blank\" href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to Developer Edition</a> to find Bugs," +
+ " Code Smells, Vulnerabilities and Security Hotspots in these files.",
+ CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE));
assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, UNANALYZED_C_KEY).get().getIntValue()).isEqualTo(10);
assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, UNANALYZED_CPP_KEY).get().getIntValue()).isEqualTo(20);
}
@@ -115,16 +123,15 @@ public class HandleUnanalyzedLanguagesStepTest {
reportReader.setMetadata(ScannerReport.Metadata.newBuilder()
.putNotAnalyzedFilesByLanguage("C", 10)
.build());
- ArgumentCaptor<CeTaskMessages.Message> argumentCaptor = ArgumentCaptor.forClass(CeTaskMessages.Message.class);
underTest.execute(new TestComputationStepContext());
verify(ceTaskMessages, times(1)).add(argumentCaptor.capture());
List<CeTaskMessages.Message> messages = argumentCaptor.getAllValues();
assertThat(messages).extracting(CeTaskMessages.Message::getText).containsExactly(
- "10 C file(s) detected during the last analysis. C code cannot be analyzed with SonarQube community " +
- "edition. Please consider <a href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to the Developer " +
- "Edition</a> to analyze this language.");
+ "10 unanalyzed C files were detected in this project during the last analysis. C cannot be analyzed with your current SonarQube edition. Please" +
+ " consider <a target=\"_blank\" href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to Developer" +
+ " Edition</a> to find Bugs, Code Smells, Vulnerabilities and Security Hotspots in this file.");
assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, UNANALYZED_C_KEY).get().getIntValue()).isEqualTo(10);
assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, UNANALYZED_CPP_KEY)).isEmpty();
}
@@ -133,19 +140,18 @@ public class HandleUnanalyzedLanguagesStepTest {
public void adds_warning_in_SQ_community_edition_if_there_are_cpp_files() {
when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.COMMUNITY));
reportReader.setMetadata(ScannerReport.Metadata.newBuilder()
- .putNotAnalyzedFilesByLanguage("C++", 9)
+ .putNotAnalyzedFilesByLanguage("C++", 1)
.build());
- ArgumentCaptor<CeTaskMessages.Message> argumentCaptor = ArgumentCaptor.forClass(CeTaskMessages.Message.class);
underTest.execute(new TestComputationStepContext());
verify(ceTaskMessages, times(1)).add(argumentCaptor.capture());
List<CeTaskMessages.Message> messages = argumentCaptor.getAllValues();
assertThat(messages).extracting(CeTaskMessages.Message::getText).containsExactly(
- "9 C++ file(s) detected during the last analysis. C++ code cannot be analyzed with SonarQube community " +
- "edition. Please consider <a href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to the Developer " +
- "Edition</a> to analyze this language.");
- assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, UNANALYZED_CPP_KEY).get().getIntValue()).isEqualTo(9);
+ "1 unanalyzed C++ file was detected in this project during the last analysis. C++ cannot be analyzed with your current SonarQube edition. Please" +
+ " consider <a target=\"_blank\" href=\"https://www.sonarqube.org/trial-request/developer-edition/?referrer=sonarqube-cpp\">upgrading to Developer" +
+ " Edition</a> to find Bugs, Code Smells, Vulnerabilities and Security Hotspots in this file.");
+ assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, UNANALYZED_CPP_KEY).get().getIntValue()).isEqualTo(1);
assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, UNANALYZED_C_KEY)).isEmpty();
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisWarningsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisWarningsStepTest.java
index 0024c611644..8f5224a0640 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisWarningsStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisWarningsStepTest.java
@@ -23,10 +23,14 @@ import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.junit.MockitoJUnitRunner;
import org.sonar.ce.task.log.CeTaskMessages;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.scanner.protocol.output.ScannerReport;
import static com.google.common.collect.ImmutableList.of;
@@ -38,11 +42,15 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
+@RunWith(MockitoJUnitRunner.class)
public class PersistAnalysisWarningsStepTest {
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+ @Captor
+ private ArgumentCaptor<List<CeTaskMessages.Message>> argumentCaptor;
+
private final CeTaskMessages ceTaskMessages = mock(CeTaskMessages.class);
private final PersistAnalysisWarningsStep underTest = new PersistAnalysisWarningsStep(reportReader, ceTaskMessages);
@@ -57,14 +65,13 @@ public class PersistAnalysisWarningsStepTest {
ScannerReport.AnalysisWarning warning2 = ScannerReport.AnalysisWarning.newBuilder().setText("warning 2").build();
ImmutableList<ScannerReport.AnalysisWarning> warnings = of(warning1, warning2);
reportReader.setAnalysisWarnings(warnings);
- ArgumentCaptor<List<CeTaskMessages.Message>> argumentCaptor = ArgumentCaptor.forClass(List.class);
underTest.execute(new TestComputationStepContext());
verify(ceTaskMessages, times(1)).addAll(argumentCaptor.capture());
assertThat(argumentCaptor.getValue())
- .extracting(CeTaskMessages.Message::getText, CeTaskMessages.Message::isDismissible)
- .containsExactly(tuple("warning 1", false), tuple("warning 2", false));
+ .extracting(CeTaskMessages.Message::getText, CeTaskMessages.Message::getType)
+ .containsExactly(tuple("warning 1", CeTaskMessageType.GENERIC), tuple("warning 2", CeTaskMessageType.GENERIC));
}
@Test
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessages.java b/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessages.java
index 7e92a255c10..53d043c934f 100644
--- a/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessages.java
+++ b/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessages.java
@@ -23,6 +23,7 @@ import java.util.Collection;
import java.util.Objects;
import javax.annotation.concurrent.Immutable;
import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.db.ce.CeTaskMessageType;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
@@ -48,19 +49,19 @@ public interface CeTaskMessages {
class Message {
private final String text;
private final long timestamp;
- private final boolean dismissible;
+ private final CeTaskMessageType type;
- public Message(String text, long timestamp, boolean dismissible) {
+ public Message(String text, long timestamp, CeTaskMessageType type) {
requireNonNull(text, "Text can't be null");
checkArgument(!text.isEmpty(), "Text can't be empty");
checkArgument(timestamp >= 0, "Timestamp can't be less than 0");
this.text = text;
this.timestamp = timestamp;
- this.dismissible = dismissible;
+ this.type = type;
}
public Message(String text, long timestamp) {
- this(text, timestamp, false);
+ this(text, timestamp, CeTaskMessageType.GENERIC);
}
public String getText() {
@@ -71,8 +72,8 @@ public interface CeTaskMessages {
return timestamp;
}
- public boolean isDismissible() {
- return dismissible;
+ public CeTaskMessageType getType() {
+ return type;
}
@Override
@@ -98,7 +99,7 @@ public interface CeTaskMessages {
return "Message{" +
"text='" + text + '\'' +
", timestamp=" + timestamp +
- ", dismissible=" + dismissible +
+ ", type=" + type +
'}';
}
}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessagesImpl.java b/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessagesImpl.java
index 153e9ce393d..f4072138cf8 100644
--- a/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessagesImpl.java
+++ b/server/sonar-ce-task/src/main/java/org/sonar/ce/task/log/CeTaskMessagesImpl.java
@@ -71,11 +71,11 @@ public class CeTaskMessagesImpl implements CeTaskMessages {
public void insert(DbSession dbSession, Message message) {
dbClient.ceTaskMessageDao().insert(dbSession, new CeTaskMessageDto()
- .setUuid(uuidFactory.create())
- .setTaskUuid(ceTask.getUuid())
- .setMessage(message.getText())
- .setDismissible(message.isDismissible())
- .setCreatedAt(message.getTimestamp()));
+ .setUuid(uuidFactory.create())
+ .setTaskUuid(ceTask.getUuid())
+ .setMessage(message.getText())
+ .setType(message.getType())
+ .setCreatedAt(message.getTimestamp()));
}
private static void checkMessage(Message message) {
diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
index 6c6f9df6e4f..4c2f24626c0 100644
--- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
+++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
@@ -115,6 +115,7 @@ public final class SqTables {
"session_tokens",
"snapshots",
"users",
+ "user_dismissed_messages",
"user_properties",
"user_roles",
"user_tokens",
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
index 9a89ca6b267..205d02d18e2 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
@@ -86,6 +86,7 @@ import org.sonar.db.user.RoleDao;
import org.sonar.db.user.SamlMessageIdDao;
import org.sonar.db.user.SessionTokensDao;
import org.sonar.db.user.UserDao;
+import org.sonar.db.user.UserDismissedMessagesDao;
import org.sonar.db.user.UserGroupDao;
import org.sonar.db.user.UserPropertiesDao;
import org.sonar.db.user.UserTokenDao;
@@ -159,6 +160,7 @@ public class DaoModule extends Module {
SchemaMigrationDao.class,
SessionTokensDao.class,
UserDao.class,
+ UserDismissedMessagesDao.class,
UserGroupDao.class,
UserPermissionDao.class,
UserPropertiesDao.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
index 022e0c4dc39..78d717373ed 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
@@ -84,6 +84,7 @@ import org.sonar.db.user.RoleDao;
import org.sonar.db.user.SamlMessageIdDao;
import org.sonar.db.user.SessionTokensDao;
import org.sonar.db.user.UserDao;
+import org.sonar.db.user.UserDismissedMessagesDao;
import org.sonar.db.user.UserGroupDao;
import org.sonar.db.user.UserPropertiesDao;
import org.sonar.db.user.UserTokenDao;
@@ -164,6 +165,7 @@ public class DbClient {
private final ProjectDao projectDao;
private final SessionTokensDao sessionTokensDao;
private final SamlMessageIdDao samlMessageIdDao;
+ private final UserDismissedMessagesDao userDismissedMessagesDao;
public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) {
this.database = database;
@@ -242,6 +244,7 @@ public class DbClient {
projectDao = getDao(map, ProjectDao.class);
sessionTokensDao = getDao(map, SessionTokensDao.class);
samlMessageIdDao = getDao(map, SamlMessageIdDao.class);
+ userDismissedMessagesDao = getDao(map, UserDismissedMessagesDao.class);
}
public DbSession openSession(boolean batch) {
@@ -534,4 +537,8 @@ public class DbClient {
return samlMessageIdDao;
}
+ public UserDismissedMessagesDao userDismissedMessagesDao() {
+ return userDismissedMessagesDao;
+ }
+
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
index bc390ff1fb1..8c1f4a3b487 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
@@ -140,6 +140,7 @@ import org.sonar.db.user.GroupMembershipMapper;
import org.sonar.db.user.RoleMapper;
import org.sonar.db.user.SamlMessageIdMapper;
import org.sonar.db.user.SessionTokenMapper;
+import org.sonar.db.user.UserDismissedMessagesMapper;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserGroupDto;
import org.sonar.db.user.UserGroupMapper;
@@ -287,6 +288,7 @@ public class MyBatis implements Startable {
SchemaMigrationMapper.class,
SessionTokenMapper.class,
SnapshotMapper.class,
+ UserDismissedMessagesMapper.class,
UserGroupMapper.class,
UserMapper.class,
UserPermissionMapper.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java
index 6604c0bb682..39c4b8e2fad 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java
@@ -20,6 +20,7 @@
package org.sonar.db.ce;
import java.util.List;
+import java.util.Optional;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
@@ -28,6 +29,10 @@ public class CeTaskMessageDao implements Dao {
getMapper(dbSession).insert(dto);
}
+ public Optional<CeTaskMessageDto> selectByUuid(DbSession dbSession, String uuid) {
+ return getMapper(dbSession).selectByUuid(uuid);
+ }
+
/**
* @return the messages for the specific task, if any, in ascending order of column {@code CREATED_AT}.
*/
@@ -35,6 +40,10 @@ public class CeTaskMessageDao implements Dao {
return getMapper(dbSession).selectByTask(taskUuid);
}
+ public void deleteByType(DbSession session, CeTaskMessageType type) {
+ getMapper(session).deleteByType(type.name());
+ }
+
private static CeTaskMessageMapper getMapper(DbSession dbSession) {
return dbSession.getMapper(CeTaskMessageMapper.class);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java
index f31bcbc2530..3eb99a6dce4 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java
@@ -35,13 +35,13 @@ public class CeTaskMessageDto {
*/
private String message;
/**
- * Timestamp the message was created. Not null
+ * Type of the message
*/
- private long createdAt;
+ private CeTaskMessageType type;
/**
- * Information if this message can be dismissed by the user
+ * Timestamp the message was created. Not null
*/
- private boolean dismissible;
+ private long createdAt;
public String getUuid() {
return uuid;
@@ -72,21 +72,21 @@ public class CeTaskMessageDto {
return this;
}
- public long getCreatedAt() {
- return createdAt;
+ public CeTaskMessageType getType() {
+ return type;
}
- public CeTaskMessageDto setCreatedAt(long createdAt) {
- this.createdAt = createdAt;
+ public CeTaskMessageDto setType(CeTaskMessageType type) {
+ this.type = type;
return this;
}
- public boolean isDismissible() {
- return dismissible;
+ public long getCreatedAt() {
+ return createdAt;
}
- public CeTaskMessageDto setDismissible(boolean dismissible) {
- this.dismissible = dismissible;
+ public CeTaskMessageDto setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
return this;
}
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java
index 5ec953f3b1d..619cd743310 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java
@@ -20,11 +20,15 @@
package org.sonar.db.ce;
import java.util.List;
+import java.util.Optional;
import org.apache.ibatis.annotations.Param;
public interface CeTaskMessageMapper {
void insert(@Param("dto") CeTaskMessageDto dto);
+ Optional<CeTaskMessageDto> selectByUuid(@Param("uuid") String uuid);
+
List<CeTaskMessageDto> selectByTask(@Param("taskUuid") String taskUuid);
+ void deleteByType(@Param("ceMessageType") String ceMessageType);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageType.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageType.java
new file mode 100644
index 00000000000..52bd4b38038
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageType.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.ce;
+
+public enum CeTaskMessageType {
+ GENERIC(false),
+ SUGGEST_DEVELOPER_EDITION_UPGRADE(true);
+
+ private final boolean dismissible;
+
+ CeTaskMessageType(boolean dismissible) {
+ this.dismissible = dismissible;
+ }
+
+ public boolean isDismissible() {
+ return dismissible;
+ }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
index 33abd185e2e..b11c7febd7f 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
@@ -434,4 +434,11 @@ class PurgeCommands {
profiler.stop();
}
+ void deleteUserDismissedMessages(String projectUuid) {
+ profiler.start("deleteUserDismissedMessages (user_dismissed_messages)");
+ purgeMapper.deleteUserDismissedMessagesByProjectUuid(projectUuid);
+ session.commit();
+ profiler.stop();
+ }
+
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
index e9a0af96f6d..bb39d91a389 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
@@ -224,6 +224,7 @@ public class PurgeDao implements Dao {
commands.deleteComponents(rootUuid);
commands.deleteComponentsByMainBranchProjectUuid(rootUuid);
commands.deleteProject(rootUuid);
+ commands.deleteUserDismissedMessages(rootUuid);
}
/**
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
index 56f09dda6b6..840533516f9 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
@@ -150,4 +150,6 @@ public interface PurgeMapper {
void deleteNewCodePeriodsByRootUuid(String rootUuid);
void deleteProjectAlmSettingsByProjectUuid(@Param("projectUuid") String projectUuid);
+
+ void deleteUserDismissedMessagesByProjectUuid(@Param("projectUuid") String projectUuid);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessageDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessageDto.java
new file mode 100644
index 00000000000..96d0d3e8219
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessageDto.java
@@ -0,0 +1,92 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.user;
+
+import org.sonar.db.ce.CeTaskMessageType;
+
+public class UserDismissedMessageDto {
+
+ private String uuid;
+ /**
+ * Uuid of the user that dismissed the message type
+ */
+ private String userUuid;
+ /**
+ * Uuid of the project for which the message type was dismissed
+ */
+ private String projectUuid;
+ /**
+ * Message type of the dismissed message
+ */
+ private CeTaskMessageType ceMessageType;
+ /**
+ * Technical creation date
+ */
+ private long createdAt;
+
+ public UserDismissedMessageDto() {
+ // nothing to do here
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public UserDismissedMessageDto setUuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+ public String getUserUuid() {
+ return userUuid;
+ }
+
+ public UserDismissedMessageDto setUserUuid(String userUuid) {
+ this.userUuid = userUuid;
+ return this;
+ }
+
+ public String getProjectUuid() {
+ return projectUuid;
+ }
+
+ public UserDismissedMessageDto setProjectUuid(String projectUuid) {
+ this.projectUuid = projectUuid;
+ return this;
+ }
+
+ public CeTaskMessageType getCeMessageType() {
+ return ceMessageType;
+ }
+
+ public UserDismissedMessageDto setCeMessageType(CeTaskMessageType ceMessageType) {
+ this.ceMessageType = ceMessageType;
+ return this;
+ }
+
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ public UserDismissedMessageDto setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesDao.java
new file mode 100644
index 00000000000..16843bbd682
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesDao.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.user;
+
+import java.util.List;
+import java.util.Optional;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+import org.sonar.db.ce.CeTaskMessageType;
+import org.sonar.db.project.ProjectDto;
+
+public class UserDismissedMessagesDao implements Dao {
+ private final System2 system2;
+
+ public UserDismissedMessagesDao(System2 system2) {
+ this.system2 = system2;
+ }
+
+ public UserDismissedMessageDto insert(DbSession session, UserDismissedMessageDto dto) {
+ long now = system2.now();
+ mapper(session).insert(dto.setCreatedAt(now));
+ return dto;
+ }
+
+ public Optional<UserDismissedMessageDto> selectByUserAndProjectAndMessageType(DbSession session, UserDto user, ProjectDto project,
+ CeTaskMessageType ceMessageType) {
+ return mapper(session).selectByUserUuidAndProjectUuidAndMessageType(user.getUuid(), project.getUuid(), ceMessageType.name());
+ }
+
+ public List<UserDismissedMessageDto> selectByUser(DbSession session, UserDto user) {
+ return mapper(session).selectByUserUuid(user.getUuid());
+ }
+
+ public void deleteByUser(DbSession session, UserDto user) {
+ mapper(session).deleteByUserUuid(user.getUuid());
+ }
+
+ public void deleteByType(DbSession session, CeTaskMessageType type) {
+ mapper(session).deleteByType(type.name());
+ }
+
+ private static UserDismissedMessagesMapper mapper(DbSession session) {
+ return session.getMapper(UserDismissedMessagesMapper.class);
+ }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesMapper.java
new file mode 100644
index 00000000000..f1fbddbfdf8
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDismissedMessagesMapper.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.user;
+
+import java.util.List;
+import java.util.Optional;
+import org.apache.ibatis.annotations.Param;
+
+public interface UserDismissedMessagesMapper {
+ void insert(@Param("dto") UserDismissedMessageDto dto);
+
+ Optional<UserDismissedMessageDto> selectByUserUuidAndProjectUuidAndMessageType(@Param("userUuid") String userUuid, @Param("projectUuid") String projectUuid,
+ @Param("ceMessageType") String ceMessageType);
+
+ List<UserDismissedMessageDto> selectByUserUuid(@Param("userUuid") String userUuid);
+
+ void deleteByUserUuid(@Param("userUuid") String userUuid);
+
+ void deleteByType(@Param("ceMessageType") String ceMessageType);
+}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml
index 1e5ca2cb2f3..360ac4425c1 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml
@@ -7,10 +7,19 @@
ctm.uuid,
ctm.task_uuid as taskUuid,
ctm.message as message,
- ctm.created_at as createdAt,
- ctm.is_dismissible as dismissible
+ ctm.message_type as type,
+ ctm.created_at as createdAt
</sql>
+ <select id="selectByUuid" resultType="org.sonar.db.ce.CeTaskMessageDto">
+ select
+ <include refid="columns"/>
+ from
+ ce_task_message ctm
+ where
+ ctm.uuid=#{uuid,jdbcType=VARCHAR}
+ </select>
+
<select id="selectByTask" resultType="org.sonar.db.ce.CeTaskMessageDto">
select
<include refid="columns"/>
@@ -28,16 +37,20 @@
uuid,
task_uuid,
message,
- created_at,
- is_dismissible
+ message_type,
+ created_at
)
values (
#{dto.uuid,jdbcType=VARCHAR},
#{dto.taskUuid,jdbcType=VARCHAR},
#{dto.message,jdbcType=VARCHAR},
- #{dto.createdAt,jdbcType=BIGINT},
- #{dto.dismissible,jdbcType=BOOLEAN}
+ #{dto.type,jdbcType=VARCHAR},
+ #{dto.createdAt,jdbcType=BIGINT}
)
</insert>
+ <delete id="deleteByType" parameterType="String">
+ delete from ce_task_message where message_type = #{ceMessageType, jdbcType=VARCHAR}
+ </delete>
+
</mapper>
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
index 9c854851d61..e4b71806473 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
@@ -504,5 +504,9 @@
<delete id="deleteLiveMeasuresByComponentUuids">
delete from live_measures where component_uuid in <foreach item="componentUuid" index="index" collection="componentUuids" open="(" separator="," close=")">#{componentUuid, jdbcType=VARCHAR}</foreach>
</delete>
+
+ <delete id="deleteUserDismissedMessagesByProjectUuid">
+ delete from user_dismissed_messages where project_uuid = #{projectUuid,jdbcType=VARCHAR}
+ </delete>
</mapper>
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserDismissedMessagesMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserDismissedMessagesMapper.xml
new file mode 100644
index 00000000000..12fad9f4643
--- /dev/null
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserDismissedMessagesMapper.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.db.user.UserDismissedMessagesMapper">
+
+ <sql id="columns">
+ udm.uuid,
+ udm.user_uuid as userUuid,
+ udm.project_uuid as projectUuid,
+ udm.message_type as ceMessageType,
+ udm.created_at as createdAt
+ </sql>
+
+ <insert id="insert" parameterType="org.sonar.db.user.UserDismissedMessageDto" useGeneratedKeys="false">
+ insert into user_dismissed_messages
+ (
+ uuid,
+ user_uuid,
+ project_uuid,
+ message_type,
+ created_at
+ )
+ values (
+ #{dto.uuid,jdbcType=VARCHAR},
+ #{dto.userUuid,jdbcType=VARCHAR},
+ #{dto.projectUuid,jdbcType=VARCHAR},
+ #{dto.ceMessageType,jdbcType=VARCHAR},
+ #{dto.createdAt,jdbcType=BIGINT}
+ )
+ </insert>
+
+ <select id="selectByUserUuidAndProjectUuidAndMessageType" resultType="org.sonar.db.user.UserDismissedMessageDto">
+ select
+ <include refid="columns"/>
+ from
+ user_dismissed_messages udm
+ where
+ udm.user_uuid=#{userUuid,jdbcType=VARCHAR} and
+ udm.project_uuid=#{projectUuid,jdbcType=VARCHAR} and
+ udm.message_type=#{ceMessageType,jdbcType=VARCHAR}
+ </select>
+
+ <select id="selectByUserUuid" resultType="org.sonar.db.user.UserDismissedMessageDto" parameterType="String">
+ select
+ <include refid="columns"/>
+ from
+ user_dismissed_messages udm
+ where
+ udm.user_uuid=#{userUuid,jdbcType=VARCHAR}
+ </select>
+
+ <delete id="deleteByUserUuid" parameterType="String">
+ delete from user_dismissed_messages where user_uuid = #{userUuid, jdbcType=VARCHAR}
+ </delete>
+
+ <delete id="deleteByType" parameterType="String">
+ delete from user_dismissed_messages where message_type = #{ceMessageType, jdbcType=VARCHAR}
+ </delete>
+
+</mapper>
diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl
index e31dc777fd9..e4f53071172 100644
--- a/server/sonar-db-dao/src/schema/schema-sq.ddl
+++ b/server/sonar-db-dao/src/schema/schema-sq.ddl
@@ -169,10 +169,11 @@ CREATE TABLE "CE_TASK_MESSAGE"(
"TASK_UUID" VARCHAR(40) NOT NULL,
"MESSAGE" VARCHAR(4000) NOT NULL,
"CREATED_AT" BIGINT NOT NULL,
- "IS_DISMISSIBLE" BOOLEAN NOT NULL
+ "MESSAGE_TYPE" VARCHAR(255) NOT NULL
);
ALTER TABLE "CE_TASK_MESSAGE" ADD CONSTRAINT "PK_CE_TASK_MESSAGE" PRIMARY KEY("UUID");
CREATE INDEX "CE_TASK_MESSAGE_TASK" ON "CE_TASK_MESSAGE"("TASK_UUID");
+CREATE INDEX "CTM_MESSAGE_TYPE" ON "CE_TASK_MESSAGE"("MESSAGE_TYPE");
CREATE TABLE "COMPONENTS"(
"UUID" VARCHAR(50) NOT NULL,
@@ -896,6 +897,18 @@ ALTER TABLE "SNAPSHOTS" ADD CONSTRAINT "PK_SNAPSHOTS" PRIMARY KEY("UUID");
CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS"("UUID");
CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS"("COMPONENT_UUID");
+CREATE TABLE "USER_DISMISSED_MESSAGES"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "USER_UUID" VARCHAR(255) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "MESSAGE_TYPE" VARCHAR(255) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "USER_DISMISSED_MESSAGES" ADD CONSTRAINT "PK_USER_DISMISSED_MESSAGES" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_USER_DISMISSED_MESSAGES" ON "USER_DISMISSED_MESSAGES"("USER_UUID", "PROJECT_UUID", "MESSAGE_TYPE");
+CREATE INDEX "UDM_PROJECT_UUID" ON "USER_DISMISSED_MESSAGES"("PROJECT_UUID");
+CREATE INDEX "UDM_MESSAGE_TYPE" ON "USER_DISMISSED_MESSAGES"("MESSAGE_TYPE");
+
CREATE TABLE "USER_PROPERTIES"(
"UUID" VARCHAR(40) NOT NULL,
"USER_UUID" VARCHAR(255) NOT NULL,
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
index d53912a6779..c6d08a72d11 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
@@ -139,6 +139,7 @@ public class CeActivityDaoTest {
.setUuid(UuidFactoryFast.getInstance().create())
.setTaskUuid(task.getUuid())
.setMessage("message_" + task.getUuid() + "_" + i)
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(task.getUuid().hashCode() + i)));
db.commit();
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java
index 149a81734cf..4ad7e016454 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java
@@ -20,6 +20,7 @@
package org.sonar.db.ce;
import java.util.List;
+import java.util.Optional;
import org.assertj.core.groups.Tuple;
import org.junit.Rule;
import org.junit.Test;
@@ -37,7 +38,7 @@ public class CeTaskMessageDaoTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private CeTaskMessageDao underTest = new CeTaskMessageDao();
+ private final CeTaskMessageDao underTest = new CeTaskMessageDao();
@Test
public void insert() {
@@ -45,13 +46,17 @@ public class CeTaskMessageDaoTest {
.setUuid("uuid_1")
.setTaskUuid("task_uuid_1")
.setMessage("message_1")
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(1_222_333L));
dbTester.getSession().commit();
- assertThat(dbTester.select("select uuid as \"UUID\", task_uuid as \"TASK_UUID\", message as \"MESSAGE\", created_at as \"CREATED_AT\" from ce_task_message"))
- .hasSize(1)
- .extracting(t -> t.get("UUID"), t -> t.get("TASK_UUID"), t -> t.get("MESSAGE"), t -> t.get("CREATED_AT"))
- .containsOnly(Tuple.tuple("uuid_1", "task_uuid_1", "message_1", 1_222_333L));
+ assertThat(
+ dbTester.select("select uuid as \"UUID\", task_uuid as \"TASK_UUID\", message as \"MESSAGE\", message_type as \"TYPE\", " +
+ "created_at as \"CREATED_AT\" from ce_task_message"))
+ .hasSize(1)
+ .extracting(t -> t.get("UUID"), t -> t.get("TASK_UUID"), t -> t.get("MESSAGE"), t -> CeTaskMessageType.valueOf((String) t.get("TYPE")),
+ t -> t.get("CREATED_AT"))
+ .containsOnly(Tuple.tuple("uuid_1", "task_uuid_1", "message_1", CeTaskMessageType.GENERIC, 1_222_333L));
}
@Test
@@ -88,11 +93,50 @@ public class CeTaskMessageDaoTest {
.isEmpty();
}
+ @Test
+ public void selectByUuid_returns_object_if_found() {
+ CeTaskMessageDto dto = insertMessage("526787a4-e8af-46c0-b340-8c48188646a5", 1, 1_222_333L);
+
+ Optional<CeTaskMessageDto> result = underTest.selectByUuid(dbTester.getSession(), dto.getUuid());
+
+ assertThat(result).isPresent();
+ assertThat(result.get().getUuid()).isEqualTo(dto.getUuid());
+ }
+
+ @Test
+ public void selectByUuid_returns_empty_if_no_record_found() {
+ Optional<CeTaskMessageDto> result = underTest.selectByUuid(dbTester.getSession(), "e2a71626-1f07-402a-aac7-dd4e0bbb4394");
+
+ assertThat(result).isNotPresent();
+ }
+
+ @Test
+ public void deleteByType_deletes_messages_of_given_type() {
+ String task1 = "task1";
+ CeTaskMessageDto[] messages = {
+ insertMessage(task1, 0, 1_222_333L, CeTaskMessageType.GENERIC),
+ insertMessage(task1, 1, 2_222_333L, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE),
+ insertMessage(task1, 2, 1_111_333L, CeTaskMessageType.GENERIC),
+ insertMessage(task1, 3, 1_222_111L, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE)
+ };
+
+ underTest.deleteByType(dbTester.getSession(), CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+ assertThat(underTest.selectByTask(dbTester.getSession(), task1))
+ .extracting(CeTaskMessageDto::getUuid)
+ .containsExactlyInAnyOrder(messages[0].getUuid(), messages[2].getUuid());
+ }
+
private CeTaskMessageDto insertMessage(String taskUuid, int i, long createdAt) {
+ return insertMessage(taskUuid, i, createdAt, CeTaskMessageType.GENERIC);
+ }
+
+ private CeTaskMessageDto insertMessage(String taskUuid, int i, long createdAt, CeTaskMessageType messageType) {
CeTaskMessageDto res = new CeTaskMessageDto()
.setUuid("message_" + i)
.setTaskUuid(taskUuid)
.setMessage("test_" + i)
+ .setType(messageType)
.setCreatedAt(createdAt);
DbSession dbSession = dbTester.getSession();
underTest.insert(dbSession, res);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java
index 596f943d0a2..020cb201687 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java
@@ -36,6 +36,7 @@ import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactoryFast;
import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
@@ -625,6 +626,22 @@ public class PurgeCommandsTest {
assertThat(dbTester.countRowsOfTable("new_code_periods")).isEqualTo(3);
}
+ @Test
+ public void deleteUserDismissedMessages_deletes_dismissed_warnings_on_project_for_all_users() {
+ UserDto user1 = dbTester.users().insertUser();
+ UserDto user2 = dbTester.users().insertUser();
+ ProjectDto project = dbTester.components().insertPrivateProjectDto();
+ ProjectDto anotherProject = dbTester.components().insertPrivateProjectDto();
+ dbTester.users().insertUserDismissedMessage(user1, project, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ dbTester.users().insertUserDismissedMessage(user2, project, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ dbTester.users().insertUserDismissedMessage(user1, anotherProject, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ PurgeCommands purgeCommands = new PurgeCommands(dbTester.getSession(), profiler, system2);
+
+ purgeCommands.deleteUserDismissedMessages(project.getUuid());
+
+ assertThat(dbTester.countRowsOfTable("user_dismissed_messages")).isEqualTo(1);
+ }
+
private void addPermissions(OrganizationDto organization, ComponentDto root) {
if (!root.isPrivate()) {
dbTester.users().insertProjectPermissionOnAnyone("foo1", root);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
index e6a9ef93314..cf3dcdb411d 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
@@ -47,7 +47,6 @@ import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
-import org.sonar.db.alm.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
import org.sonar.db.ce.CeActivityDto;
import org.sonar.db.ce.CeQueueDto;
@@ -55,6 +54,7 @@ import org.sonar.db.ce.CeQueueDto.Status;
import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.CeTaskInputDao;
import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
@@ -78,6 +78,8 @@ import org.sonar.db.project.ProjectDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.source.FileSourceDto;
+import org.sonar.db.user.UserDismissedMessageDto;
+import org.sonar.db.user.UserDto;
import org.sonar.db.webhook.WebhookDeliveryLiteDto;
import org.sonar.db.webhook.WebhookDto;
@@ -110,16 +112,16 @@ public class PurgeDaoTest {
private static final String PROJECT_UUID = "P1";
- private System2 system2 = mock(System2.class);
+ private final System2 system2 = mock(System2.class);
@Rule
public DbTester db = DbTester.create(system2);
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private DbClient dbClient = db.getDbClient();
- private DbSession dbSession = db.getSession();
- private PurgeDao underTest = db.getDbClient().purgeDao();
+ private final DbClient dbClient = db.getDbClient();
+ private final DbSession dbSession = db.getSession();
+ private final PurgeDao underTest = db.getDbClient().purgeDao();
@Test
public void purge_failed_ce_tasks() {
@@ -703,6 +705,25 @@ public class PurgeDaoTest {
}
@Test
+ public void delete_rows_in_user_dismissed_messages_when_deleting_project() {
+ UserDto user1 = db.users().insertUser();
+ UserDto user2 = db.users().insertUser();
+ ProjectDto project = db.components().insertPrivateProjectDto();
+ ProjectDto anotherProject = db.components().insertPrivateProjectDto();
+
+ UserDismissedMessageDto msg1 = db.users().insertUserDismissedMessage(user1, project, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ UserDismissedMessageDto msg2 = db.users().insertUserDismissedMessage(user2, project, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ UserDismissedMessageDto msg3 = db.users().insertUserDismissedMessage(user1, anotherProject, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+ assertThat(uuidsIn("user_dismissed_messages")).containsOnly(msg1.getUuid(), msg2.getUuid(), msg3.getUuid());
+
+ underTest.deleteProject(dbSession, project.getUuid());
+ dbSession.commit();
+
+ assertThat(uuidsIn("user_dismissed_messages")).containsOnly(msg3.getUuid());
+ }
+
+ @Test
public void delete_tasks_in_ce_queue_when_deleting_project() {
ComponentDto projectToBeDeleted = db.components().insertPrivateProject();
ComponentDto anotherLivingProject = db.components().insertPrivateProject();
@@ -1629,6 +1650,7 @@ public class PurgeDaoTest {
.setUuid(UuidFactoryFast.getInstance().create())
.setTaskUuid(uuid)
.setMessage("key_" + uuid.hashCode() + i)
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(2_333_444L + i))
.forEach(dto -> dbClient.ceTaskMessageDao().insert(dbSession, dto));
dbSession.commit();
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDismissedMessagesDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDismissedMessagesDaoTest.java
new file mode 100644
index 00000000000..e7b640ab0bd
--- /dev/null
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDismissedMessagesDaoTest.java
@@ -0,0 +1,152 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.user;
+
+import java.util.List;
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeTaskMessageType;
+import org.sonar.db.project.ProjectDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.db.ce.CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE;
+
+public class UserDismissedMessagesDaoTest {
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private final UserDismissedMessagesDao underTest = db.getDbClient().userDismissedMessagesDao();
+
+ @Test
+ public void insert_user_dismissed_message() {
+ ProjectDto project = db.components().insertPrivateProjectDto();
+ UserDto user = db.users().insertUser();
+ UserDismissedMessageDto dto = newDto(project, user);
+
+ underTest.insert(db.getSession(), dto);
+
+ Optional<UserDismissedMessageDto> dtoFromDb = underTest.selectByUserAndProjectAndMessageType(db.getSession(), user, project, dto.getCeMessageType());
+ assertThat(dtoFromDb).isPresent();
+ assertThat(dtoFromDb.get().getUuid()).isEqualTo(dto.getUuid());
+ assertThat(dtoFromDb.get().getUserUuid()).isEqualTo(dto.getUserUuid());
+ assertThat(dtoFromDb.get().getProjectUuid()).isEqualTo(dto.getProjectUuid());
+ assertThat(dtoFromDb.get().getCeMessageType()).isEqualTo(dto.getCeMessageType());
+ assertThat(dtoFromDb.get().getCeMessageType().isDismissible()).isEqualTo(dto.getCeMessageType().isDismissible());
+ assertThat(dtoFromDb.get().getCreatedAt()).isEqualTo(dto.getCreatedAt());
+ }
+
+ @Test
+ public void selectByUserAndProjectAndMessageType_returns_object_if_record_found() {
+ UserDto user = db.users().insertUser();
+ ProjectDto project = db.components().insertPrivateProjectDto();
+ db.users().insertUserDismissedMessage(user, project, CeTaskMessageType.GENERIC);
+
+ Optional<UserDismissedMessageDto> result = underTest.selectByUserAndProjectAndMessageType(db.getSession(), user, project, CeTaskMessageType.GENERIC);
+
+ assertThat(result).isPresent();
+ assertThat(result.get().getUserUuid()).isEqualTo(user.getUuid());
+ assertThat(result.get().getProjectUuid()).isEqualTo(project.getUuid());
+ assertThat(result.get().getCeMessageType()).isEqualTo(CeTaskMessageType.GENERIC);
+ }
+
+ @Test
+ public void selectByUserAndProjectAndMessageType_returns_absent_if_no_record_found() {
+ UserDto user = db.users().insertUser();
+ ProjectDto project = db.components().insertPrivateProjectDto();
+ db.users().insertUserDismissedMessage(user, project, CeTaskMessageType.GENERIC);
+
+ Optional<UserDismissedMessageDto> result = underTest.selectByUserAndProjectAndMessageType(db.getSession(), user, project, SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+ assertThat(result).isNotPresent();
+ }
+
+ @Test
+ public void selectByUserUuid_returns_all_dismissed_messages_of_a_user() {
+ UserDto user1 = db.users().insertUser();
+ UserDto user2 = db.users().insertUser();
+ ProjectDto project1 = db.components().insertPrivateProjectDto();
+ ProjectDto project2 = db.components().insertPrivateProjectDto();
+ db.users().insertUserDismissedMessage(user1, project1, CeTaskMessageType.GENERIC);
+ db.users().insertUserDismissedMessage(user1, project2, CeTaskMessageType.GENERIC);
+ UserDismissedMessageDto dto1 = db.users().insertUserDismissedMessage(user2, project1, CeTaskMessageType.GENERIC);
+ UserDismissedMessageDto dto2 = db.users().insertUserDismissedMessage(user2, project2, CeTaskMessageType.GENERIC);
+
+ List<UserDismissedMessageDto> result = underTest.selectByUser(db.getSession(), user2);
+
+ assertThat(result).hasSize(2);
+ assertThat(result).extracting(UserDismissedMessageDto::getUuid, UserDismissedMessageDto::getUserUuid, UserDismissedMessageDto::getProjectUuid,
+ UserDismissedMessageDto::getCeMessageType)
+ .containsExactlyInAnyOrder(
+ tuple(dto1.getUuid(), user2.getUuid(), project1.getUuid(), CeTaskMessageType.GENERIC),
+ tuple(dto2.getUuid(), user2.getUuid(), project2.getUuid(), CeTaskMessageType.GENERIC));
+ }
+
+ @Test
+ public void deleteByUserUuid_removes_dismiss_warning_data_of_a_user() {
+ UserDto user1 = db.users().insertUser();
+ UserDto user2 = db.users().insertUser();
+ ProjectDto project1 = db.components().insertPrivateProjectDto();
+ ProjectDto project2 = db.components().insertPrivateProjectDto();
+ db.users().insertUserDismissedMessage(user1, project1, CeTaskMessageType.GENERIC);
+ db.users().insertUserDismissedMessage(user1, project2, CeTaskMessageType.GENERIC);
+ db.users().insertUserDismissedMessage(user2, project1, CeTaskMessageType.GENERIC);
+ db.users().insertUserDismissedMessage(user2, project2, CeTaskMessageType.GENERIC);
+
+ underTest.deleteByUser(db.getSession(), user2);
+
+ assertThat(underTest.selectByUser(db.getSession(), user1)).hasSize(2);
+ assertThat(underTest.selectByUser(db.getSession(), user2)).isEmpty();
+ }
+
+ @Test
+ public void deleteByUserUuid_removes_dismissed_messages_of_that_type() {
+ UserDto user1 = db.users().insertUser();
+ UserDto user2 = db.users().insertUser();
+ ProjectDto project1 = db.components().insertPrivateProjectDto();
+ ProjectDto project2 = db.components().insertPrivateProjectDto();
+ UserDismissedMessageDto dto1 = db.users().insertUserDismissedMessage(user1, project1, CeTaskMessageType.GENERIC);
+ db.users().insertUserDismissedMessage(user1, project2, SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ db.users().insertUserDismissedMessage(user2, project1, SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ UserDismissedMessageDto dto2 = db.users().insertUserDismissedMessage(user2, project2, CeTaskMessageType.GENERIC);
+
+ underTest.deleteByType(db.getSession(), SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+ assertThat(underTest.selectByUser(db.getSession(), user1))
+ .extracting(UserDismissedMessageDto::getUuid)
+ .containsExactly(dto1.getUuid());
+ assertThat(underTest.selectByUser(db.getSession(), user2))
+ .extracting(UserDismissedMessageDto::getUuid)
+ .containsExactly(dto2.getUuid());
+ }
+
+ public static UserDismissedMessageDto newDto(ProjectDto project, UserDto user) {
+ return new UserDismissedMessageDto()
+ .setUuid(Uuids.createFast())
+ .setCeMessageType(CeTaskMessageType.GENERIC)
+ .setUserUuid(user.getUuid())
+ .setProjectUuid(project.getUuid());
+ }
+}
diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java
index 079e15fe109..d3b3d4540e3 100644
--- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java
+++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java
@@ -32,11 +32,13 @@ import org.sonar.core.util.Uuids;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.GroupPermissionDto;
import org.sonar.db.permission.OrganizationPermission;
import org.sonar.db.permission.UserPermissionDto;
+import org.sonar.db.project.ProjectDto;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
@@ -403,4 +405,15 @@ public class UserDbTester {
return dto;
}
+ public final UserDismissedMessageDto insertUserDismissedMessage(UserDto userDto, ProjectDto projectDto, CeTaskMessageType messageType) {
+ UserDismissedMessageDto dto = new UserDismissedMessageDto()
+ .setUuid(Uuids.create())
+ .setUserUuid(userDto.getUuid())
+ .setProjectUuid(projectDto.getUuid())
+ .setCeMessageType(messageType);
+ db.getDbClient().userDismissedMessagesDao().insert(db.getSession(), dto);
+ db.commit();
+ return dto;
+ }
+
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTable.java
new file mode 100644
index 00000000000..091289b5cc5
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTable.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.platform.db.migration.version.v85;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddIndexOnMessageTypeColumnOfCeTaskMessageTable extends DdlChange {
+ private static final String TABLE = "ce_task_message";
+
+ public AddIndexOnMessageTypeColumnOfCeTaskMessageTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("ctm_message_type")
+ .addColumn("message_type")
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddIsDismissibleColumnToCeTaskMessageTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTable.java
index 674ae2f2b81..f8cf9f6c7fe 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddIsDismissibleColumnToCeTaskMessageTable.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTable.java
@@ -21,29 +21,29 @@ package org.sonar.server.platform.db.migration.version.v85;
import java.sql.SQLException;
import org.sonar.db.Database;
-import org.sonar.server.platform.db.migration.def.BooleanColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;
-import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
-public class AddIsDismissibleColumnToCeTaskMessageTable extends DdlChange {
+public class AddMessageTypeColumnToCeTaskMessageTable extends DdlChange {
private static final String TABLE = "ce_task_message";
- private static final String NEW_COLUMN = "is_dismissible";
- private static final BooleanColumnDef IS_DISMISSIBLE = newBooleanColumnDefBuilder()
- .setColumnName(NEW_COLUMN)
+ private static final VarcharColumnDef MESSAGE_TYPE = newVarcharColumnDefBuilder()
+ .setColumnName("message_type")
.setIsNullable(true)
+ .setLimit(255)
.build();
- public AddIsDismissibleColumnToCeTaskMessageTable(Database db) {
+ public AddMessageTypeColumnToCeTaskMessageTable(Database db) {
super(db);
}
@Override
public void execute(Context context) throws SQLException {
context.execute(new AddColumnsBuilder(getDialect(), TABLE)
- .addColumn(IS_DISMISSIBLE)
+ .addColumn(MESSAGE_TYPE)
.build());
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTable.java
new file mode 100644
index 00000000000..82b91108df1
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTable.java
@@ -0,0 +1,102 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.platform.db.migration.version.v85;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class CreateUserDismissedMessagesTable extends DdlChange {
+
+ private static final String TABLE_NAME = "user_dismissed_messages";
+
+ private static final VarcharColumnDef UUID_COLUMN = newVarcharColumnDefBuilder()
+ .setColumnName("uuid")
+ .setIsNullable(false)
+ .setLimit(UUID_SIZE)
+ .build();
+
+ private static final VarcharColumnDef USER_UUID_COLUMN = newVarcharColumnDefBuilder()
+ .setColumnName("user_uuid")
+ .setIsNullable(false)
+ .setLimit(VarcharColumnDef.USER_UUID_SIZE)
+ .build();
+
+ private static final VarcharColumnDef PROJECT_UUID = newVarcharColumnDefBuilder()
+ .setColumnName("project_uuid")
+ .setIsNullable(false)
+ .setLimit(UUID_SIZE)
+ .build();
+
+ private static final VarcharColumnDef MESSAGE_TYPE = newVarcharColumnDefBuilder()
+ .setColumnName("message_type")
+ .setIsNullable(false)
+ .setLimit(255)
+ .build();
+
+ private static final BigIntegerColumnDef CREATED_AT = newBigIntegerColumnDefBuilder()
+ .setColumnName("created_at")
+ .setIsNullable(false)
+ .build();
+
+ public CreateUserDismissedMessagesTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME)
+ .addPkColumn(UUID_COLUMN)
+ .addColumn(USER_UUID_COLUMN)
+ .addColumn(PROJECT_UUID)
+ .addColumn(MESSAGE_TYPE)
+ .addColumn(CREATED_AT)
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE_NAME)
+ .setName("uniq_user_dismissed_messages")
+ .setUnique(true)
+ .addColumn(USER_UUID_COLUMN)
+ .addColumn(PROJECT_UUID)
+ .addColumn(MESSAGE_TYPE)
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE_NAME)
+ .setName("udm_project_uuid")
+ .addColumn(PROJECT_UUID)
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE_NAME)
+ .setName("udm_message_type")
+ .addColumn(MESSAGE_TYPE)
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/DbVersion85.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/DbVersion85.java
index 80ea17d33da..0396ecac844 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/DbVersion85.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/DbVersion85.java
@@ -47,9 +47,11 @@ public class DbVersion85 implements DbVersion {
.add(4016, "Add 'type' column to 'plugins' table", AddTypeToPlugins.class)
.add(4017, "Populate 'type' column in 'plugins' table", PopulateTypeInPlugins.class)
.add(4018, "Alter 'type' column in 'plugins' to not nullable", AlterTypeInPluginNotNullable.class)
- .add(4019, "Add 'is_dismissible' column to `ce_task_message` table", AddIsDismissibleColumnToCeTaskMessageTable.class)
- .add(4020, "Populate 'is_dismissible' column of `ce_task_message` table", PopulateIsDismissibleColumnOfCeTaskMessageTable.class)
- .add(4021, "Make 'is_dismissible' column not nullable for `ce_task_message` table", MakeIsDismissibleColumnNotNullableOnCeTaskMessageTable.class)
- ;
+ .add(4019, "Add 'message_type' column to 'ce_task_message' table", AddMessageTypeColumnToCeTaskMessageTable.class)
+ .add(4020, "Populate 'message_type' column of 'ce_task_message' table", PopulateMessageTypeColumnOfCeTaskMessageTable.class)
+ .add(4021, "Make 'message_type' column not nullable for `ce_task_message` table", MakeMessageTypeColumnNotNullableOnCeTaskMessageTable.class)
+ .add(4022, "Add index on 'message_type' column of `ce_task_message` table", AddIndexOnMessageTypeColumnOfCeTaskMessageTable.class)
+ .add(4023, "Create 'user_dismissed_messages' table", CreateUserDismissedMessagesTable.class
+ );
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/MakeIsDismissibleColumnNotNullableOnCeTaskMessageTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTable.java
index 6b285203b4b..aad98f9c76a 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/MakeIsDismissibleColumnNotNullableOnCeTaskMessageTable.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTable.java
@@ -21,30 +21,27 @@ package org.sonar.server.platform.db.migration.version.v85;
import java.sql.SQLException;
import org.sonar.db.Database;
-import org.sonar.server.platform.db.migration.def.BooleanColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;
-import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
-public class MakeIsDismissibleColumnNotNullableOnCeTaskMessageTable extends DdlChange {
-
- private static final String TABLE = "ce_task_message";
- private static final String NEW_COLUMN = "is_dismissible";
-
- private static final BooleanColumnDef IS_DISMISSIBLE = newBooleanColumnDefBuilder()
- .setColumnName(NEW_COLUMN)
+public class MakeMessageTypeColumnNotNullableOnCeTaskMessageTable extends DdlChange {
+ private static final VarcharColumnDef MESSAGE_TYPE = newVarcharColumnDefBuilder()
+ .setColumnName("message_type")
.setIsNullable(false)
+ .setLimit(255)
.build();
- public MakeIsDismissibleColumnNotNullableOnCeTaskMessageTable(Database db) {
+ public MakeMessageTypeColumnNotNullableOnCeTaskMessageTable(Database db) {
super(db);
}
@Override
public void execute(Context context) throws SQLException {
- context.execute(new AlterColumnsBuilder(getDialect(), TABLE)
- .updateColumn(IS_DISMISSIBLE)
+ context.execute(new AlterColumnsBuilder(getDialect(), "ce_task_message")
+ .updateColumn(MESSAGE_TYPE)
.build());
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateIsDismissibleColumnOfCeTaskMessageTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTable.java
index 63863b29912..00e358dcff7 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateIsDismissibleColumnOfCeTaskMessageTable.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTable.java
@@ -24,20 +24,18 @@ import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.step.MassUpdate;
-public class PopulateIsDismissibleColumnOfCeTaskMessageTable extends DataChange {
-
- public PopulateIsDismissibleColumnOfCeTaskMessageTable(Database db) {
+public class PopulateMessageTypeColumnOfCeTaskMessageTable extends DataChange {
+ public PopulateMessageTypeColumnOfCeTaskMessageTable(Database db) {
super(db);
}
@Override
- protected void execute(Context context) throws SQLException {
+ protected void execute(DataChange.Context context) throws SQLException {
MassUpdate massUpdate = context.prepareMassUpdate();
- massUpdate.select("select ctm.uuid from ce_task_message ctm where ctm.is_dismissible is null");
- massUpdate.update("update ce_task_message set is_dismissible = ? where uuid = ?");
-
+ massUpdate.select("select ctm.uuid from ce_task_message ctm where ctm.message_type is null");
+ massUpdate.update("update ce_task_message set message_type = ? where uuid = ?");
massUpdate.execute((row, update) -> {
- update.setBoolean(1, false);
+ update.setString(1, "GENERIC");
update.setString(2, row.getString(1));
return true;
});
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddIsDismissibleColumnToCeTaskMessageTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest.java
index bc51cb03288..b5c648a79bc 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddIsDismissibleColumnToCeTaskMessageTableTest.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest.java
@@ -17,7 +17,6 @@
* 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.platform.db.migration.version.v85;
import java.sql.SQLException;
@@ -26,20 +25,19 @@ import org.junit.Test;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.DdlChange;
-import static java.sql.Types.BOOLEAN;
-
-public class AddIsDismissibleColumnToCeTaskMessageTableTest {
+public class AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest {
+ private static final String TABLE_NAME = "ce_task_message";
+ private static final String INDEX_NAME = "ctm_message_type";
@Rule
- public CoreDbTester db = CoreDbTester.createForSchema(AddIsDismissibleColumnToCeTaskMessageTableTest.class, "schema.sql");
+ public CoreDbTester db = CoreDbTester.createForSchema(AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest.class, "schema.sql");
- DdlChange underTest = new AddIsDismissibleColumnToCeTaskMessageTable(db.database());
+ DdlChange underTest = new AddIndexOnMessageTypeColumnOfCeTaskMessageTable(db.database());
@Test
- public void add_column() throws SQLException {
+ public void add_index() throws SQLException {
underTest.execute();
- db.assertColumnDefinition("ce_task_message", "is_dismissible", BOOLEAN, null, true);
+ db.assertIndex(TABLE_NAME, INDEX_NAME, "message_type");
}
-
}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest.java
new file mode 100644
index 00000000000..0ab65645f12
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.platform.db.migration.version.v85;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AddMessageTypeColumnToCeTaskMessageTableTest {
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(AddMessageTypeColumnToCeTaskMessageTableTest.class, "schema.sql");
+
+ private final DdlChange underTest = new AddMessageTypeColumnToCeTaskMessageTable(db.database());
+
+ @Before
+ public void setup() {
+ insertTaskMessage(1L, "a1");
+ insertTaskMessage(2L, "a2");
+ insertTaskMessage(3L, "a3");
+ }
+
+ @Test
+ public void add_message_type_column_to_ce_task_message_table() throws SQLException {
+ underTest.execute();
+
+ db.assertColumnDefinition("ce_task_message", "message_type", Types.VARCHAR, 255, true);
+
+ assertThat(db.countSql("select count(uuid) from ce_task_message")).isEqualTo(3);
+ }
+
+ private void insertTaskMessage(Long id, String message) {
+ db.executeInsert("ce_task_message",
+ "uuid", "uuid-" + id,
+ "task_uuid", "task-uuid-" + id,
+ "message", message,
+ "created_at", System2.INSTANCE.now());
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTableTest.java
new file mode 100644
index 00000000000..427fda8f9c4
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/CreateUserDismissedMessagesTableTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.platform.db.migration.version.v85;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.BIGINT;
+import static java.sql.Types.VARCHAR;
+
+public class CreateUserDismissedMessagesTableTest {
+ private static final String TABLE_NAME = "user_dismissed_messages";
+
+ @Rule
+ public CoreDbTester dbTester = CoreDbTester.createEmpty();
+
+ private final CreateUserDismissedMessagesTable underTest = new CreateUserDismissedMessagesTable(dbTester.database());
+
+ @Test
+ public void table_has_been_created() throws SQLException {
+ underTest.execute();
+
+ dbTester.assertTableExists(TABLE_NAME);
+ dbTester.assertPrimaryKey(TABLE_NAME, "pk_user_dismissed_messages", "uuid");
+ dbTester.assertColumnDefinition(TABLE_NAME, "uuid", VARCHAR, 40, false);
+ dbTester.assertColumnDefinition(TABLE_NAME, "user_uuid", VARCHAR, 255, false);
+ dbTester.assertColumnDefinition(TABLE_NAME, "project_uuid", VARCHAR, 40, false);
+ dbTester.assertColumnDefinition(TABLE_NAME, "message_type", VARCHAR, 255, false);
+ dbTester.assertColumnDefinition(TABLE_NAME, "created_at", BIGINT, null, false);
+ dbTester.assertUniqueIndex(TABLE_NAME, "uniq_user_dismissed_messages", "user_uuid",
+ "project_uuid", "message_type");
+ dbTester.assertIndex(TABLE_NAME, "udm_project_uuid", "project_uuid");
+ dbTester.assertIndex(TABLE_NAME, "udm_message_type", "message_type");
+ }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/MakeIsDismissibleColumnNotNullableOnCeTaskMessageTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest.java
index 5f49ab61888..b559adf60ef 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/MakeIsDismissibleColumnNotNullableOnCeTaskMessageTableTest.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest.java
@@ -25,20 +25,19 @@ import org.junit.Test;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.MigrationStep;
-import static java.sql.Types.BOOLEAN;
+import static java.sql.Types.VARCHAR;
-public class MakeIsDismissibleColumnNotNullableOnCeTaskMessageTableTest {
+public class MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest {
@Rule
- public CoreDbTester db = CoreDbTester.createForSchema(MakeIsDismissibleColumnNotNullableOnCeTaskMessageTableTest.class, "schema.sql");
+ public CoreDbTester db = CoreDbTester.createForSchema(MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest.class, "schema.sql");
- private MigrationStep underTest = new MakeIsDismissibleColumnNotNullableOnCeTaskMessageTable(db.database());
+ private final MigrationStep underTest = new MakeMessageTypeColumnNotNullableOnCeTaskMessageTable(db.database());
@Test
- public void ce_task_message_column_is_not_null() throws SQLException {
+ public void message_type_column_is_not_null() throws SQLException {
underTest.execute();
- db.assertColumnDefinition("ce_task_message", "is_dismissible", BOOLEAN, null, false);
+ db.assertColumnDefinition("ce_task_message", "message_type", VARCHAR, 255, false);
}
-
}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateIsDismissibleColumnOfCeTaskMessageTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateIsDismissibleColumnOfCeTaskMessageTableTest.java
deleted file mode 100644
index fb17fc524e8..00000000000
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateIsDismissibleColumnOfCeTaskMessageTableTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.platform.db.migration.version.v85;
-
-import java.sql.SQLException;
-import javax.annotation.Nullable;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.core.util.Uuids;
-import org.sonar.db.CoreDbTester;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class PopulateIsDismissibleColumnOfCeTaskMessageTableTest {
-
- @Rule
- public CoreDbTester db = CoreDbTester.createForSchema(PopulateIsDismissibleColumnOfCeTaskMessageTableTest.class, "schema.sql");
-
- private PopulateIsDismissibleColumnOfCeTaskMessageTable underTest = new PopulateIsDismissibleColumnOfCeTaskMessageTable(db.database());
-
- @Test
- public void execute_migration() throws SQLException {
- insertCeTaskMessage(null);
- insertCeTaskMessage(null);
- insertCeTaskMessage(null);
-
- underTest.execute();
-
- assertIsDismissibleValuesAreAllFalse();
- }
-
- @Test
- public void migrate_not_already_updated_rows() throws SQLException {
- insertCeTaskMessage(false);
- insertCeTaskMessage(false);
- insertCeTaskMessage(null);
-
- underTest.execute();
-
- assertIsDismissibleValuesAreAllFalse();
- }
-
- @Test
- public void migration_is_reentrant() throws SQLException {
- insertCeTaskMessage(null);
-
- underTest.execute();
- underTest.execute();
-
- assertIsDismissibleValuesAreAllFalse();
- }
-
- private void assertIsDismissibleValuesAreAllFalse() {
- assertThat(db.select("select is_dismissible as \"IS_DISMISSIBLE\" from ce_task_message")
- .stream()
- .map(rows -> rows.get("IS_DISMISSIBLE")))
- .containsOnly(false);
- }
-
- private String insertCeTaskMessage(@Nullable Boolean isDissmisible) {
- String uuid = Uuids.createFast();
- db.executeInsert("CE_TASK_MESSAGE",
- "UUID", uuid,
- "TASK_UUID", Uuids.createFast(),
- "MESSAGE", "message-" + uuid,
- "IS_DISMISSIBLE", isDissmisible,
- "CREATED_AT", System.currentTimeMillis());
- return uuid;
- }
-}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest.java
new file mode 100644
index 00000000000..b5cfcec501d
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest.java
@@ -0,0 +1,73 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.platform.db.migration.version.v85;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class PopulateMessageTypeColumnOfCeTaskMessageTableTest {
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateMessageTypeColumnOfCeTaskMessageTableTest.class, "schema.sql");
+
+ private final DataChange underTest = new PopulateMessageTypeColumnOfCeTaskMessageTable(db.database());
+
+ @Test
+ public void add_message_type_column_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from ce_task_message")).isZero();
+ }
+
+ @Test
+ public void add_message_type_column_on_non_empty_table() throws SQLException {
+ insertTaskMessage(1L, "msg1");
+ insertTaskMessage(2L, "msg2");
+ insertTaskMessage(3L, "msg3");
+ insertTaskMessage(4L, "msg4");
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from ce_task_message")).isEqualTo(4);
+ assertThat(db.select("select uuid, task_uuid, message, message_type from ce_task_message"))
+ .extracting(m -> m.get("UUID"), m -> m.get("TASK_UUID"), m -> m.get("MESSAGE"), m -> m.get("MESSAGE_TYPE"))
+ .containsExactlyInAnyOrder(
+ tuple("uuid-1", "task-uuid-1", "msg1", "GENERIC"),
+ tuple("uuid-2", "task-uuid-2", "msg2", "GENERIC"),
+ tuple("uuid-3", "task-uuid-3", "msg3", "GENERIC"),
+ tuple("uuid-4", "task-uuid-4", "msg4", "GENERIC"));
+ }
+
+ private void insertTaskMessage(Long id, String message) {
+ db.executeInsert("ce_task_message",
+ "uuid", "uuid-" + id,
+ "task_uuid", "task-uuid-" + id,
+ "message", message,
+ "created_at", System2.INSTANCE.now());
+ }
+
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest/schema.sql
new file mode 100644
index 00000000000..4200aab9747
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddIndexOnMessageTypeColumnOfCeTaskMessageTableTest/schema.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "CE_TASK_MESSAGE"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "MESSAGE" VARCHAR(4000) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "MESSAGE_TYPE" VARCHAR(255) NOT NULL
+);
+ALTER TABLE "CE_TASK_MESSAGE" ADD CONSTRAINT "PK_CE_TASK_MESSAGE" PRIMARY KEY("UUID");
+CREATE INDEX "CE_TASK_MESSAGE_TASK" ON "CE_TASK_MESSAGE"("TASK_UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest/schema.sql
new file mode 100644
index 00000000000..67237e59da5
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/AddMessageTypeColumnToCeTaskMessageTableTest/schema.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "CE_TASK_MESSAGE" (
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "MESSAGE" VARCHAR(4000) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+
+ CONSTRAINT "CE_TASK_MESSAGE" PRIMARY KEY ("UUID")
+);
+CREATE INDEX "CE_TASK_MESSAGE_TASK" ON "CE_TASK_MESSAGE" ("TASK_UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest/schema.sql
new file mode 100644
index 00000000000..c46fbbfd8c6
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/MakeMessageTypeColumnNotNullableOnCeTaskMessageTableTest/schema.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "CE_TASK_MESSAGE"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "MESSAGE" VARCHAR(4000) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "MESSAGE_TYPE" VARCHAR(255)
+);
+ALTER TABLE "CE_TASK_MESSAGE" ADD CONSTRAINT "PK_CE_TASK_MESSAGE" PRIMARY KEY("UUID");
+CREATE INDEX "CE_TASK_MESSAGE_TASK" ON "CE_TASK_MESSAGE"("TASK_UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest/schema.sql
new file mode 100644
index 00000000000..c46fbbfd8c6
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/PopulateMessageTypeColumnOfCeTaskMessageTableTest/schema.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "CE_TASK_MESSAGE"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "MESSAGE" VARCHAR(4000) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "MESSAGE_TYPE" VARCHAR(255)
+);
+ALTER TABLE "CE_TASK_MESSAGE" ADD CONSTRAINT "PK_CE_TASK_MESSAGE" PRIMARY KEY("UUID");
+CREATE INDEX "CE_TASK_MESSAGE_TASK" ON "CE_TASK_MESSAGE"("TASK_UUID");
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/startup/UpgradeSuggestionsCleaner.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/startup/UpgradeSuggestionsCleaner.java
new file mode 100644
index 00000000000..335eed30863
--- /dev/null
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/startup/UpgradeSuggestionsCleaner.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.startup;
+
+import org.picocontainer.Startable;
+import org.sonar.api.SonarEdition;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.ce.CeTaskMessageType;
+
+/**
+ * Clean up messages (like removing upgrade suggestions after an edition upgrade)
+ */
+@ServerSide
+public class UpgradeSuggestionsCleaner implements Startable {
+
+ private static final Logger LOGGER = Loggers.get(UpgradeSuggestionsCleaner.class);
+
+ private final DbClient dbClient;
+ private final SonarRuntime sonarRuntime;
+
+ public UpgradeSuggestionsCleaner(DbClient dbClient, SonarRuntime sonarRuntime) {
+ this.dbClient = dbClient;
+ this.sonarRuntime = sonarRuntime;
+ }
+
+ @Override
+ public void start() {
+ if (sonarRuntime.getEdition() == SonarEdition.COMMUNITY) {
+ return;
+ }
+
+ deleteUpgradeMessageDismissals();
+ }
+
+ private void deleteUpgradeMessageDismissals() {
+ LOGGER.info("Dismissed messages cleanup");
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ dbClient.userDismissedMessagesDao().deleteByType(dbSession, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ dbClient.ceTaskMessageDao().deleteByType(dbSession, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ dbSession.commit();
+ }
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+}
diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/startup/UpgradeSuggestionsCleanerTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/startup/UpgradeSuggestionsCleanerTest.java
new file mode 100644
index 00000000000..c5c8a032776
--- /dev/null
+++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/startup/UpgradeSuggestionsCleanerTest.java
@@ -0,0 +1,148 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.startup;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.SonarEdition;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
+import org.sonar.db.user.UserDismissedMessageDto;
+import org.sonar.db.user.UserDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(DataProviderRunner.class)
+public class UpgradeSuggestionsCleanerTest {
+ private static final String TASK_UUID = "b8d564dd-4ceb-4dba-8a3d-5fafa2d72cdf";
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final SonarRuntime sonarRuntime = mock(SonarRuntime.class);
+ private final UpgradeSuggestionsCleaner underTest = new UpgradeSuggestionsCleaner(dbTester.getDbClient(), sonarRuntime);
+
+ private UserDto user;
+
+ @Before
+ public void setup() {
+ user = dbTester.users().insertUser();
+ }
+
+ @DataProvider
+ public static Object[][] editionsWithCleanup() {
+ return new Object[][] {
+ {SonarEdition.DEVELOPER},
+ {SonarEdition.ENTERPRISE},
+ {SonarEdition.DATACENTER}
+ };
+ }
+
+ @DataProvider
+ public static Object[][] allEditions() {
+ return new Object[][] {
+ {SonarEdition.COMMUNITY},
+ {SonarEdition.DEVELOPER},
+ {SonarEdition.ENTERPRISE},
+ {SonarEdition.DATACENTER}
+ };
+ }
+
+ @Test
+ @UseDataProvider("editionsWithCleanup")
+ public void start_cleans_up_obsolete_upgrade_suggestions(SonarEdition edition) {
+ when(sonarRuntime.getEdition()).thenReturn(edition);
+ insertCeTaskMessage("ctm1", CeTaskMessageType.GENERIC, "msg1");
+ insertCeTaskMessage("ctm2", CeTaskMessageType.GENERIC, "msg2");
+ insertCeTaskMessage("ctm3", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE, "upgrade-msg-1");
+ insertInUserDismissedMessages("u1", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ insertInUserDismissedMessages("u2", CeTaskMessageType.GENERIC);
+
+ underTest.start();
+ underTest.stop();
+
+ assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID))
+ .extracting(CeTaskMessageDto::getUuid)
+ .containsExactly("ctm1", "ctm2");
+ assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user))
+ .extracting(UserDismissedMessageDto::getUuid)
+ .containsExactly("u2");
+ }
+
+ @Test
+ public void start_does_nothing_in_community_edition() {
+ when(sonarRuntime.getEdition()).thenReturn(SonarEdition.COMMUNITY);
+ insertCeTaskMessage("ctm1", CeTaskMessageType.GENERIC, "msg1");
+ insertCeTaskMessage("ctm2", CeTaskMessageType.GENERIC, "msg2");
+ insertCeTaskMessage("ctm3", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE, "upgrade-msg-1");
+ insertInUserDismissedMessages("u1", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ insertInUserDismissedMessages("u2", CeTaskMessageType.GENERIC);
+
+ underTest.start();
+
+ assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID))
+ .extracting(CeTaskMessageDto::getUuid)
+ .containsExactly("ctm1", "ctm2", "ctm3");
+ assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user))
+ .extracting(UserDismissedMessageDto::getUuid)
+ .containsExactlyInAnyOrder("u1", "u2");
+ }
+
+ @Test
+ @UseDataProvider("allEditions")
+ public void start_does_nothing_when_no_suggest_upgrade_messages(SonarEdition edition) {
+ when(sonarRuntime.getEdition()).thenReturn(edition);
+
+ underTest.start();
+
+ assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID)).isEmpty();
+ assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user)).isEmpty();
+ }
+
+ private void insertCeTaskMessage(String uuid, CeTaskMessageType messageType, String msg) {
+ CeTaskMessageDto dto = new CeTaskMessageDto()
+ .setUuid(uuid)
+ .setMessage(msg)
+ .setType(messageType)
+ .setTaskUuid(TASK_UUID);
+ dbTester.getDbClient().ceTaskMessageDao().insert(dbTester.getSession(), dto);
+ dbTester.getSession().commit();
+ }
+
+ private void insertInUserDismissedMessages(String uuid, CeTaskMessageType messageType) {
+ UserDismissedMessageDto dto = new UserDismissedMessageDto()
+ .setUuid(uuid)
+ .setUserUuid(user.getUuid())
+ .setProjectUuid("PROJECT_1")
+ .setCeMessageType(messageType);
+ dbTester.getDbClient().userDismissedMessagesDao().insert(dbTester.getSession(), dto);
+ dbTester.getSession().commit();
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/AnalysisStatusAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/AnalysisStatusAction.java
index 9178c1f7788..55b49a97127 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/AnalysisStatusAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/AnalysisStatusAction.java
@@ -128,7 +128,7 @@ public class AnalysisStatusAction implements CeWsAction {
.map(dto -> AnalysisStatusWsResponse.Warning.newBuilder()
.setKey(dto.getUuid())
.setMessage(dto.getMessage())
- .setDismissable(dto.isDismissible())
+ .setDismissable(dto.getType().isDismissible())
.build())
.collect(Collectors.toList());
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/CeWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/CeWsModule.java
index 10cca51ce45..a08737c40bf 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/CeWsModule.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/CeWsModule.java
@@ -46,6 +46,7 @@ public class CeWsModule extends Module {
TaskFormatter.class,
TaskAction.class,
TaskTypesAction.class,
- WorkerCountAction.class);
+ WorkerCountAction.class,
+ DismissAnalysisWarningAction.class);
}
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/DismissAnalysisWarningAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/DismissAnalysisWarningAction.java
new file mode 100644
index 00000000000..ac9b8270682
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/DismissAnalysisWarningAction.java
@@ -0,0 +1,115 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.ce.ws;
+
+import java.util.Optional;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.user.UserDismissedMessageDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.server.exceptions.NotFoundException.checkFound;
+
+public class DismissAnalysisWarningAction implements CeWsAction {
+
+ private static final String MESSAGE_NOT_FOUND = "Message '%s' not found.";
+ private static final String MESSAGE_CANNOT_BE_DISMISSED = "Message '%s' cannot be dismissed.";
+ private static final String PARAM_COMPONENT_KEY = "component";
+ private static final String PARAM_MESSAGE_KEY = "warning";
+
+ private final UserSession userSession;
+ private final DbClient dbClient;
+ private final ComponentFinder componentFinder;
+
+ public DismissAnalysisWarningAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder) {
+ this.userSession = userSession;
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ WebService.NewAction action = controller
+ .createAction("dismiss_analysis_warning")
+ .setPost(true)
+ .setDescription("Permanently dismiss a specific analysis warning. Requires authentication and 'Browse' permission on the specified project.")
+ .setSince("8.5")
+ .setInternal(true)
+ .setHandler(this);
+
+ action.createParam(PARAM_COMPONENT_KEY)
+ .setDescription("Key of the project")
+ .setRequired(true)
+ .setExampleValue(Uuids.UUID_EXAMPLE_01);
+ action.createParam(PARAM_MESSAGE_KEY)
+ .setDescription("Key of the warning")
+ .setRequired(true)
+ .setExampleValue(Uuids.UUID_EXAMPLE_02);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.checkLoggedIn();
+ String userLogin = requireNonNull(userSession.getLogin());
+ String projectKey = request.mandatoryParam(PARAM_COMPONENT_KEY);
+ String messageKey = request.mandatoryParam(PARAM_MESSAGE_KEY);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ UserDto user = getUser(dbSession, userLogin);
+ ProjectDto project = componentFinder.getProjectByKey(dbSession, projectKey);
+ userSession.checkProjectPermission(UserRole.USER, project);
+
+ CeTaskMessageDto messageDto = dbClient.ceTaskMessageDao()
+ .selectByUuid(dbSession, messageKey)
+ .orElseThrow(() -> new NotFoundException(format(MESSAGE_NOT_FOUND, messageKey)));
+ if (!messageDto.getType().isDismissible()) {
+ throw new IllegalArgumentException(format(MESSAGE_CANNOT_BE_DISMISSED, messageKey));
+ }
+
+ Optional<UserDismissedMessageDto> result = dbClient.userDismissedMessagesDao().selectByUserAndProjectAndMessageType(dbSession, user, project, messageDto.getType());
+ if (!result.isPresent()) {
+ dbClient.userDismissedMessagesDao().insert(dbSession, new UserDismissedMessageDto()
+ .setUuid(Uuids.create())
+ .setUserUuid(user.getUuid())
+ .setProjectUuid(project.getUuid())
+ .setCeMessageType(messageDto.getType()));
+ dbSession.commit();
+ }
+
+ response.noContent();
+ }
+ }
+
+ private UserDto getUser(DbSession dbSession, String userLogin) {
+ return checkFound(dbClient.userDao().selectByLogin(dbSession, userLogin), "User '%s' not found", userLogin);
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DeactivateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DeactivateAction.java
index ac5b47ecbfe..1a201a392e8 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DeactivateAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DeactivateAction.java
@@ -27,8 +27,6 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.NewAction;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -49,8 +47,6 @@ import static org.sonar.server.exceptions.NotFoundException.checkFound;
public class DeactivateAction implements UsersWsAction {
- private static final Logger LOGGER = Loggers.get(DeactivateAction.class);
-
private static final String PARAM_LOGIN = "login";
private final DbClient dbClient;
@@ -109,6 +105,7 @@ public class DeactivateAction implements UsersWsAction {
dbClient.userPropertiesDao().deleteByUser(dbSession, user);
dbClient.almPatDao().deleteByUser(dbSession, user);
dbClient.sessionTokensDao().deleteByUser(dbSession, user);
+ dbClient.userDismissedMessagesDao().deleteByUser(dbSession, user);
dbClient.userDao().deactivateUser(dbSession, user);
userIndexer.commitAndIndex(dbSession, user);
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
index 2a1dad02869..4b61f8968f5 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
@@ -40,6 +40,7 @@ import org.sonar.db.ce.CeActivityDto.Status;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
@@ -247,6 +248,7 @@ public class ActivityActionTest {
.setUuid("uuid_" + taskUuid + "_" + i)
.setTaskUuid(taskUuid)
.setMessage("m_" + taskUuid + "_" + i)
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(taskUuid.hashCode() + i)));
db.commit();
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/AnalysisStatusActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/AnalysisStatusActionTest.java
index 906096f8666..62b473c1122 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/AnalysisStatusActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/AnalysisStatusActionTest.java
@@ -30,6 +30,7 @@ import org.sonar.db.DbTester;
import org.sonar.db.ce.CeActivityDto;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
@@ -92,7 +93,7 @@ public class AnalysisStatusActionTest {
SnapshotDto analysis = db.components().insertSnapshot(project);
CeActivityDto activity = insertActivity("task-uuid" + counter++, project, SUCCESS, analysis, REPORT);
CeTaskMessageDto taskMessage = createTaskMessage(activity, WARNING_IN_MAIN);
- CeTaskMessageDto taskMessageDismissible = createTaskMessage(activity, "Dismissible warning", true);
+ CeTaskMessageDto taskMessageDismissible = createTaskMessage(activity, "Dismissible warning", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
Ce.AnalysisStatusWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -288,7 +289,7 @@ public class AnalysisStatusActionTest {
.setUuid("AU-Tpxb--iU5OvuD2FLy")
.setTaskUuid(activity.getUuid())
.setMessage("Property \"sonar.jacoco.reportPaths\" is no longer supported. Use JaCoCo xml report and sonar-jacoco plugin.")
- .setDismissible(false)
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(counter);
db.getDbClient().ceTaskMessageDao().insert(db.getSession(), ceTaskMessage);
db.commit();
@@ -329,15 +330,15 @@ public class AnalysisStatusActionTest {
}
private CeTaskMessageDto createTaskMessage(CeActivityDto activity, String warning) {
- return createTaskMessage(activity, warning, false);
+ return createTaskMessage(activity, warning, CeTaskMessageType.GENERIC);
}
- private CeTaskMessageDto createTaskMessage(CeActivityDto activity, String warning, boolean dismissible) {
+ private CeTaskMessageDto createTaskMessage(CeActivityDto activity, String warning, CeTaskMessageType messageType) {
CeTaskMessageDto ceTaskMessageDto = new CeTaskMessageDto()
.setUuid("m-uuid-" + counter++)
.setTaskUuid(activity.getUuid())
.setMessage(warning)
- .setDismissible(dismissible)
+ .setType(messageType)
.setCreatedAt(counter);
db.getDbClient().ceTaskMessageDao().insert(db.getSession(), ceTaskMessageDto);
db.commit();
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java
index 3ab8d1413bd..0329920b4f2 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java
@@ -31,6 +31,6 @@ public class CeWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new CeWsModule().configure(container);
- assertThat(container.size()).isEqualTo(19 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER);
+ assertThat(container.size()).isEqualTo(20 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER);
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java
index 935276da9d1..ea4df38a0b1 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java
@@ -34,6 +34,7 @@ import org.sonar.db.ce.CeActivityDto;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
@@ -255,6 +256,7 @@ public class ComponentActionTest {
.setUuid("uuid_" + i)
.setTaskUuid(activity.getUuid())
.setMessage("m_" + i)
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(i)));
db.commit();
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/DismissAnalysisWarningActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/DismissAnalysisWarningActionTest.java
new file mode 100644
index 00000000000..045dd360e8e
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/DismissAnalysisWarningActionTest.java
@@ -0,0 +1,208 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.ce.ws;
+
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeActivityDto;
+import org.sonar.db.ce.CeQueueDto;
+import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.component.TestComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.junit.Assert.assertThrows;
+import static org.sonar.db.ce.CeActivityDto.Status.SUCCESS;
+import static org.sonar.db.ce.CeTaskTypes.REPORT;
+
+public class DismissAnalysisWarningActionTest {
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private static int counter = 1;
+
+ private final WsActionTester underTest = new WsActionTester(new DismissAnalysisWarningAction(userSession, db.getDbClient(), TestComponentFinder.from(db)));
+
+ @Test
+ public void definition() {
+ WebService.Action def = underTest.getDef();
+ assertThat(def.key()).isEqualTo("dismiss_analysis_warning");
+ assertThat(def.isInternal()).isTrue();
+ assertThat(def.isPost()).isTrue();
+ assertThat(def.params()).extracting(WebService.Param::key, WebService.Param::isRequired).containsOnly(
+ tuple("component", true),
+ tuple("warning", true));
+ }
+
+ @Test
+ public void return_401_if_user_is_not_logged_in() {
+ userSession.anonymous();
+ TestRequest request = underTest.newRequest()
+ .setParam("component", "6653f062-7c03-4b55-bcd2-0dac67640c4d")
+ .setParam("warning", "55c40b35-4145-4b78-bdf2-dfb242c25f15");
+
+ assertThrows("Authentication is required", UnauthorizedException.class, request::execute);
+ }
+
+ @Test
+ public void return_403_if_user_has_no_browse_permission_on_private_project() {
+ ProjectDto project = db.components().insertPrivateProjectDto();
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user);
+
+ TestRequest request = underTest.newRequest()
+ .setParam("component", project.getKee())
+ .setParam("warning", "55c40b35-4145-4b78-bdf2-dfb242c25f15");
+
+ assertThrows("Insufficient privileges", ForbiddenException.class, request::execute);
+ }
+
+ @Test
+ public void return_204_on_success() {
+ UserDto user = db.users().insertUser();
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.logIn(user).addProjectPermission(UserRole.USER, project);
+ SnapshotDto analysis = db.components().insertSnapshot(project);
+ CeActivityDto activity = insertActivity("task-uuid" + counter++, project, SUCCESS, analysis, REPORT);
+ CeTaskMessageDto taskMessageDismissible = createTaskMessage(activity, "dismissable warning", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+ TestResponse response = underTest.newRequest()
+ .setParam("component", project.getKey())
+ .setParam("warning", taskMessageDismissible.getUuid())
+ .execute();
+
+ assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(db.select("select * from user_dismissed_messages"))
+ .extracting("USER_UUID", "PROJECT_UUID", "MESSAGE_TYPE")
+ .containsExactly(tuple(userSession.getUuid(), project.uuid(), CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE.name()));
+ }
+
+ @Test
+ public void is_idempotent() {
+ UserDto user = db.users().insertUser();
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.logIn(user).addProjectPermission(UserRole.USER, project);
+ SnapshotDto analysis = db.components().insertSnapshot(project);
+ CeActivityDto activity = insertActivity("task-uuid" + counter++, project, SUCCESS, analysis, REPORT);
+ CeTaskMessageDto taskMessageDismissible = createTaskMessage(activity, "dismissable warning", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+ underTest.newRequest()
+ .setParam("component", project.getKey())
+ .setParam("warning", taskMessageDismissible.getUuid())
+ .execute();
+ TestResponse response = underTest.newRequest()
+ .setParam("component", project.getKey())
+ .setParam("warning", taskMessageDismissible.getUuid())
+ .execute();
+
+ assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(db.select("select * from user_dismissed_messages"))
+ .extracting("USER_UUID", "PROJECT_UUID", "MESSAGE_TYPE")
+ .containsExactly(tuple(userSession.getUuid(), project.uuid(), CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE.name()));
+ }
+
+ @Test
+ public void returns_400_if_warning_is_not_dismissable() {
+ UserDto user = db.users().insertUser();
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.logIn(user).addProjectPermission(UserRole.USER, project);
+ SnapshotDto analysis = db.components().insertSnapshot(project);
+ CeActivityDto activity = insertActivity("task-uuid" + counter++, project, SUCCESS, analysis, REPORT);
+ CeTaskMessageDto taskMessage = createTaskMessage(activity, "generic warning");
+
+ TestRequest request = underTest.newRequest()
+ .setParam("component", project.getKey())
+ .setParam("warning", taskMessage.getUuid());
+
+ assertThrows(format("Message '%s' cannot be dismissed.", taskMessage.getUuid()), IllegalArgumentException.class, request::execute);
+ assertThat(db.countRowsOfTable("USER_DISMISSED_MESSAGES")).isZero();
+ }
+
+ @Test
+ public void returns_404_if_warning_does_not_exist() {
+ UserDto user = db.users().insertUser();
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.logIn(user).addProjectPermission(UserRole.USER, project);
+ SnapshotDto analysis = db.components().insertSnapshot(project);
+ insertActivity("task-uuid" + counter++, project, SUCCESS, analysis, REPORT);
+ String warningUuid = "78d1e2ff-3e67-4037-ba58-0d57d5f88e44";
+
+ TestRequest request = underTest.newRequest()
+ .setParam("component", project.getKey())
+ .setParam("warning", warningUuid);
+
+ assertThrows(format("Message '%s' not found", warningUuid), NotFoundException.class, request::execute);
+ assertThat(db.countRowsOfTable("USER_DISMISSED_MESSAGES")).isZero();
+ }
+
+ private CeTaskMessageDto createTaskMessage(CeActivityDto activity, String warning) {
+ return createTaskMessage(activity, warning, CeTaskMessageType.GENERIC);
+ }
+
+ private CeTaskMessageDto createTaskMessage(CeActivityDto activity, String warning, CeTaskMessageType messageType) {
+ CeTaskMessageDto ceTaskMessageDto = new CeTaskMessageDto()
+ .setUuid("m-uuid-" + counter++)
+ .setTaskUuid(activity.getUuid())
+ .setMessage(warning)
+ .setType(messageType)
+ .setCreatedAt(counter);
+ db.getDbClient().ceTaskMessageDao().insert(db.getSession(), ceTaskMessageDto);
+ db.commit();
+ return ceTaskMessageDto;
+ }
+
+ private CeActivityDto insertActivity(String taskUuid, ComponentDto component, CeActivityDto.Status status, @Nullable SnapshotDto analysis, String taskType) {
+ CeQueueDto queueDto = new CeQueueDto();
+ queueDto.setTaskType(taskType);
+ queueDto.setComponent(component);
+ queueDto.setUuid(taskUuid);
+ CeActivityDto activityDto = new CeActivityDto(queueDto);
+ activityDto.setStatus(status);
+ activityDto.setExecutionTimeMs(500L);
+ activityDto.setAnalysisUuid(analysis == null ? null : analysis.getUuid());
+ activityDto.setExecutedAt((long) counter++);
+ activityDto.setTaskType(taskType);
+ activityDto.setComponentUuid(component.uuid());
+ db.getDbClient().ceActivityDao().insert(db.getSession(), activityDto);
+ db.getSession().commit();
+ return activityDto;
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/TaskActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/TaskActionTest.java
index 737e218d99a..0e2f2e010eb 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/TaskActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/TaskActionTest.java
@@ -38,6 +38,7 @@ import org.sonar.db.ce.CeActivityDto;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
@@ -74,9 +75,9 @@ public class TaskActionTest {
private OrganizationDto organization;
private ComponentDto privateProject;
private ComponentDto publicProject;
- private TaskFormatter formatter = new TaskFormatter(db.getDbClient(), System2.INSTANCE);
- private TaskAction underTest = new TaskAction(db.getDbClient(), formatter, userSession);
- private WsActionTester ws = new WsActionTester(underTest);
+ private final TaskFormatter formatter = new TaskFormatter(db.getDbClient(), System2.INSTANCE);
+ private final TaskAction underTest = new TaskAction(db.getDbClient(), formatter, userSession);
+ private final WsActionTester ws = new WsActionTester(underTest);
@Before
public void setUp() {
@@ -125,6 +126,7 @@ public class TaskActionTest {
.setUuid("u_" + i)
.setTaskUuid(queueDto.getUuid())
.setMessage("m_" + i)
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(queueDto.getUuid().hashCode() + i)));
db.commit();
@@ -393,7 +395,7 @@ public class TaskActionTest {
CeActivityDto task = createAndPersistArchivedTask(publicProject);
expectedException.expect(ForbiddenException.class);
-
+
call(task.getUuid());
}
@@ -488,6 +490,7 @@ public class TaskActionTest {
.setUuid(UuidFactoryFast.getInstance().create())
.setTaskUuid(task.getUuid())
.setMessage("msg_" + task.getUuid() + "_" + i)
+ .setType(CeTaskMessageType.GENERIC)
.setCreatedAt(task.getUuid().hashCode() + i);
db.getDbClient().ceTaskMessageDao().insert(db.getSession(), res);
db.getSession().commit();
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java
index caaecbb3ac1..60b84eee1cc 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java
@@ -30,15 +30,18 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.db.ce.CeTaskMessageType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.template.PermissionTemplateDto;
import org.sonar.db.permission.template.PermissionTemplateUserDto;
+import org.sonar.db.project.ProjectDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.SessionTokenDto;
+import org.sonar.db.user.UserDismissedMessageDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
@@ -71,7 +74,7 @@ import static org.sonar.test.JsonAssert.assertJson;
public class DeactivateActionTest {
- private System2 system2 = new AlwaysIncreasingSystem2();
+ private final System2 system2 = new AlwaysIncreasingSystem2();
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -85,11 +88,11 @@ public class DeactivateActionTest {
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
- private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
- private DbClient dbClient = db.getDbClient();
- private UserIndexer userIndexer = new UserIndexer(dbClient, es.client());
- private DbSession dbSession = db.getSession();
- private WsActionTester ws = new WsActionTester(new DeactivateAction(dbClient, userIndexer, userSession,
+ private final DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
+ private final DbClient dbClient = db.getDbClient();
+ private final UserIndexer userIndexer = new UserIndexer(dbClient, es.client());
+ private final DbSession dbSession = db.getSession();
+ private final WsActionTester ws = new WsActionTester(new DeactivateAction(dbClient, userIndexer, userSession,
new UserJsonWriter(userSession), defaultOrganizationProvider));
@Test
@@ -265,9 +268,9 @@ public class DeactivateActionTest {
logInAsSystemAdministrator();
UserDto user = db.users().insertUser();
SessionTokenDto sessionToken1 = db.users().insertSessionToken(user);
- SessionTokenDto sessionToken2 =db.users().insertSessionToken(user);
+ SessionTokenDto sessionToken2 = db.users().insertSessionToken(user);
UserDto anotherUser = db.users().insertUser();
- SessionTokenDto sessionToken3 =db.users().insertSessionToken(anotherUser);
+ SessionTokenDto sessionToken3 = db.users().insertSessionToken(anotherUser);
deactivate(user.getLogin());
@@ -277,6 +280,27 @@ public class DeactivateActionTest {
}
@Test
+ public void deactivate_user_deletes_his_dismissed_messages() {
+ logInAsSystemAdministrator();
+ ProjectDto project1 = db.components().insertPrivateProjectDto();
+ ProjectDto project2 = db.components().insertPrivateProjectDto();
+ UserDto user = db.users().insertUser();
+
+ db.users().insertUserDismissedMessage(user, project1, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ db.users().insertUserDismissedMessage(user, project2, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ UserDto anotherUser = db.users().insertUser();
+ UserDismissedMessageDto msg3 = db.users().insertUserDismissedMessage(anotherUser, project1, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ UserDismissedMessageDto msg4 = db.users().insertUserDismissedMessage(anotherUser, project2, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+ deactivate(user.getLogin());
+
+ assertThat(db.getDbClient().userDismissedMessagesDao().selectByUser(dbSession, user)).isEmpty();
+ assertThat(db.getDbClient().userDismissedMessagesDao().selectByUser(dbSession, anotherUser))
+ .extracting(UserDismissedMessageDto::getUuid)
+ .containsExactlyInAnyOrder(msg3.getUuid(), msg4.getUuid());
+ }
+
+ @Test
public void user_cannot_deactivate_itself_on_sonarqube() {
UserDto user = db.users().insertUser();
userSession.logIn(user.getLogin()).setSystemAdministrator();
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
index 6c46862da77..0d17d7971d3 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
@@ -44,6 +44,7 @@ import org.sonar.server.startup.RegisterPlugins;
import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
import org.sonar.server.user.DoPrivileged;
import org.sonar.server.user.ThreadLocalUserSession;
+import org.sonar.server.startup.UpgradeSuggestionsCleaner;
public class PlatformLevelStartup extends PlatformLevel {
public PlatformLevelStartup(PlatformLevel parent) {
@@ -70,7 +71,8 @@ public class PlatformLevelStartup extends PlatformLevel {
RegisterQualityProfiles.class,
RegisterPermissionTemplates.class,
RenameDeprecatedPropertyKeys.class,
- CeQueueCleaner.class);
+ CeQueueCleaner.class,
+ UpgradeSuggestionsCleaner.class);
// RegisterServletFilters makes the WebService engine of Level4 served by the MasterServletFilter, therefor it
// must be started after all the other startup tasks
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java
index 67637749b54..bec63dbd7e5 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java
@@ -144,6 +144,22 @@ public class CeService extends BaseService {
/**
*
* This is part of the internal API.
+ * This is a POST request.
+ * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/ce/dismiss_analysis_warning">Further information about this action online (including a response example)</a>
+ * @since 8.5
+ */
+ public void dismissAnalysisWarning(DismissAnalysisWarningRequest request) {
+ call(
+ new PostRequest(path("dismiss_analysis_warning"))
+ .setParam("component", request.getComponent())
+ .setParam("warning", request.getWarning())
+ .setMediaType(MediaTypes.JSON)
+ ).content();
+ }
+
+ /**
+ *
+ * This is part of the internal API.
* This is a GET request.
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/ce/info">Further information about this action online (including a response example)</a>
* @since 7.2
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/DismissAnalysisWarningRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/DismissAnalysisWarningRequest.java
new file mode 100644
index 00000000000..6c1defaa710
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/DismissAnalysisWarningRequest.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.sonarqube.ws.client.ce;
+
+import javax.annotation.Generated;
+
+/**
+ * This is part of the internal API.
+ * This is a POST request.
+ * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/ce/dismiss_analysis_warning">Further information about this action online (including a response example)</a>
+ * @since 8.5
+ */
+@Generated("sonar-ws-generator")
+public class DismissAnalysisWarningRequest {
+
+ private String component;
+ private String warning;
+
+ /**
+ * Example value: "AU-Tpxb--iU5OvuD2FLy"
+ */
+ public DismissAnalysisWarningRequest setComponent(String component) {
+ this.component = component;
+ return this;
+ }
+
+ public String getComponent() {
+ return component;
+ }
+
+ /**
+ * Example value: "AU-TpxcA-iU5OvuD2FLz"
+ */
+ public DismissAnalysisWarningRequest setWarning(String warning) {
+ this.warning = warning;
+ return this;
+ }
+
+ public String getWarning() {
+ return warning;
+ }
+}