private String severity;
private boolean manualSeverity;
private String message;
+ private byte[] messageFormattings;
private Integer line;
private Double gap;
private Long effort;
.setLine(issue.line())
.setLocations((DbIssues.Locations) issue.getLocations())
.setMessage(issue.message())
+ .setMessageFormattings((byte[]) null)
.setGap(issue.gap())
.setEffort(issue.effortInMinutes())
.setResolution(issue.resolution())
.setLine(issue.line())
.setLocations((DbIssues.Locations) issue.getLocations())
.setMessage(issue.message())
+ .setMessageFormattings((byte[]) null)
.setGap(issue.gap())
.setEffort(issue.effortInMinutes())
.setResolution(issue.resolution())
return this;
}
+ public IssueDto setMessageFormattings(@Nullable byte[] messageFormattings) {
+ this.messageFormattings = messageFormattings;
+ return this;
+ }
+
+ public IssueDto setMessageFormattings(@Nullable DbIssues.MessageFormattings messageFormattings) {
+ if (messageFormattings == null) {
+ this.messageFormattings = null;
+ } else {
+ this.messageFormattings = messageFormattings.toByteArray();
+ }
+ return this;
+ }
+
+ @CheckForNull
+ public byte[] getMessageFormattings() {
+ return messageFormattings;
+ }
+
@CheckForNull
public Integer getLine() {
return line;
optional sonarqube.db.commons.TextRange text_range = 2;
optional string msg = 3;
optional string checksum = 4;
+ repeated MessageFormatting msgFormattings = 5;
+}
+message MessageFormattings {
+ repeated MessageFormatting messageFormatting = 1;
+}
+
+message MessageFormatting {
+ required int32 start = 1;
+ required int32 end = 2;
+ required MessageFormattingType type = 3;
+}
+
+enum MessageFormattingType {
+ CODE = 0;
}
"ISSUE_TYPE" TINYINT,
"FROM_HOTSPOT" BOOLEAN,
"QUICK_FIX_AVAILABLE" BOOLEAN,
- "RULE_DESCRIPTION_CONTEXT_KEY" CHARACTER VARYING(50)
+ "RULE_DESCRIPTION_CONTEXT_KEY" CHARACTER VARYING(50),
+ "MESSAGE_FORMATTINGS" BINARY LARGE OBJECT
);
ALTER TABLE "ISSUES" ADD CONSTRAINT "PK_ISSUES" PRIMARY KEY("KEE");
CREATE INDEX "ISSUES_ASSIGNEE" ON "ISSUES"("ASSIGNEE" NULLS FIRST);
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.db.protobuf.DbIssues;
import org.sonar.db.rule.RuleDto;
import static org.assertj.core.api.Assertions.assertThat;
private static final String TEST_CONTEXT_KEY = "test_context_key";
+ private static final DbIssues.MessageFormattings EXAMPLE_MESSAGE_FORMATTINGS = DbIssues.MessageFormattings.newBuilder()
+ .addMessageFormatting(DbIssues.MessageFormatting.newBuilder().setStart(0).setEnd(1).setType(DbIssues.MessageFormattingType.CODE)
+ .build()).build();
+
@Test
- public void set_issue_fields() {
+ public void toDefaultIssue_set_issue_fields() {
Date createdAt = DateUtils.addDays(new Date(), -5);
Date updatedAt = DateUtils.addDays(new Date(), -3);
Date closedAt = DateUtils.addDays(new Date(), -1);
.setLine(6)
.setSeverity("BLOCKER")
.setMessage("message")
+ .setMessageFormattings(EXAMPLE_MESSAGE_FORMATTINGS)
.setManualSeverity(true)
.setAssigneeUuid("perceval")
.setAuthorLogin("pierre")
assertThat(issue.line()).isEqualTo(6);
assertThat(issue.severity()).isEqualTo("BLOCKER");
assertThat(issue.message()).isEqualTo("message");
+ //assertThat(issue.getMessageFormatting()).isEqualTo(EXAMPLE_MESSAGE_FORMATTING); //TODO fix later SONAR-17592
assertThat(issue.manualSeverity()).isTrue();
assertThat(issue.assignee()).isEqualTo("perceval");
assertThat(issue.authorLogin()).isEqualTo("pierre");
containsExactly("key", RuleType.BUG.getDbConstant(), RuleKey.of("repo", "rule"));
assertThat(issueDto).extracting(IssueDto::getIssueCreationDate, IssueDto::getIssueCloseDate,
- IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt, IssueDto::getCreatedAt)
+ IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt, IssueDto::getCreatedAt)
.containsExactly(dateNow, dateNow, dateNow, dateNow.getTime(), now, now);
assertThat(issueDto).extracting(IssueDto::getLine, IssueDto::getMessage,
- IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
+ IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
.containsExactly(1, "message", 1.0, 1L, Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED, "BLOCKER");
assertThat(issueDto).extracting(IssueDto::getTags, IssueDto::getAuthorLogin)
.containsExactly(Set.of("todo"), "admin");
assertThat(issueDto).extracting(IssueDto::isManualSeverity, IssueDto::getChecksum, IssueDto::getAssigneeUuid,
- IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey,
- IssueDto::getModuleUuidPath, IssueDto::getProjectUuid, IssueDto::getProjectKey,
- IssueDto::getRuleUuid)
+ IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey,
+ IssueDto::getModuleUuidPath, IssueDto::getProjectUuid, IssueDto::getProjectKey,
+ IssueDto::getRuleUuid)
.containsExactly(true, "123", "123", true, "123", "componentKey",
"path/to/module/uuid", "123", "projectKey", "ruleUuid");
containsExactly("key", RuleType.BUG.getDbConstant(), RuleKey.of("repo", "rule"));
assertThat(issueDto).extracting(IssueDto::getIssueCreationDate, IssueDto::getIssueCloseDate,
- IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt)
+ IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt)
.containsExactly(dateNow, dateNow, dateNow, dateNow.getTime(), now);
assertThat(issueDto).extracting(IssueDto::getLine, IssueDto::getMessage,
- IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
+ IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
.containsExactly(1, "message", 1.0, 1L, Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED, "BLOCKER");
assertThat(issueDto).extracting(IssueDto::getTags, IssueDto::getAuthorLogin)
.containsExactly(Set.of("todo"), "admin");
assertThat(issueDto).extracting(IssueDto::isManualSeverity, IssueDto::getChecksum, IssueDto::getAssigneeUuid,
- IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey,
- IssueDto::getModuleUuidPath, IssueDto::getProjectUuid, IssueDto::getProjectKey)
+ IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey,
+ IssueDto::getModuleUuidPath, IssueDto::getProjectUuid, IssueDto::getProjectKey)
.containsExactly(true, "123", "123", true, "123", "componentKey",
"path/to/module/uuid", "123", "projectKey");
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.v98;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.def.BlobColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddMessageFormattingsColumnToIssueTable extends DdlChange {
+ private static final String TABLE_NAME = "issues";
+ private static final String COLUMN_NAME = "message_formattings";
+
+ public AddMessageFormattingsColumnToIssueTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ try (Connection c = getDatabase().getDataSource().getConnection()) {
+ if (!DatabaseUtils.tableColumnExists(c, TABLE_NAME, COLUMN_NAME)) {
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
+ .addColumn(BlobColumnDef.newBlobColumnDefBuilder().setColumnName(COLUMN_NAME).setIsNullable(true).build())
+ .build());
+ }
+ }
+ }
+}
.add(6702, "Move project measure variations to values", MoveProjectMeasureVariationToValue.class)
.add(6703, "Drop project measure variation column", DropProjectMeasureVariationColumn.class)
.add(6704, "Update sonar-users group description", UpsertSonarUsersDescription.class)
+ .add(6705, "Add message_formattings column to issue table", AddMessageFormattingsColumnToIssueTable.class)
;
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.v98;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+public class AddMessageFormattingsColumnToIssueTableTest {
+ private static final String TABLE_NAME = "issues";
+ private static final String COLUMN_NAME = "message_formattings";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddMessageFormattingsColumnToIssueTableTest.class, "schema.sql");
+
+ private final AddMessageFormattingsColumnToIssueTable underTest = new AddMessageFormattingsColumnToIssueTable(db.database());
+
+ @Test
+ public void migration_should_add_column() throws SQLException {
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BLOB, null, true);
+ }
+
+ @Test
+ public void migration_should_be_reentrant() throws SQLException {
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ underTest.execute();
+ // re-entrant
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BLOB, null, true);
+ }
+
+}
--- /dev/null
+CREATE TABLE "ISSUES"(
+ "KEE" VARCHAR(50) NOT NULL,
+ "RULE_UUID" VARCHAR(40),
+ "SEVERITY" VARCHAR(10),
+ "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+ "MESSAGE" VARCHAR(4000),
+ "LINE" INTEGER,
+ "GAP" DOUBLE,
+ "STATUS" VARCHAR(20),
+ "RESOLUTION" VARCHAR(20),
+ "CHECKSUM" VARCHAR(1000),
+ "REPORTER" VARCHAR(255),
+ "ASSIGNEE" VARCHAR(255),
+ "AUTHOR_LOGIN" VARCHAR(255),
+ "ACTION_PLAN_KEY" VARCHAR(50),
+ "ISSUE_ATTRIBUTES" VARCHAR(4000),
+ "EFFORT" INTEGER,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "ISSUE_CREATION_DATE" BIGINT,
+ "ISSUE_UPDATE_DATE" BIGINT,
+ "ISSUE_CLOSE_DATE" BIGINT,
+ "TAGS" VARCHAR(4000),
+ "COMPONENT_UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "LOCATIONS" BLOB,
+ "ISSUE_TYPE" TINYINT,
+ "FROM_HOTSPOT" BOOLEAN,
+ CONSTRAINT pk_issues PRIMARY KEY (KEE)
+);
\ No newline at end of file