package org.sonar.ce.task.projectanalysis.issue;
import java.util.Collection;
+import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.core.issue.AnticipatedTransition;
public interface AnticipatedTransitionRepository {
- Collection<AnticipatedTransition> getAnticipatedTransitionByProjectUuid(String projectKey, String filePath);
+ Collection<AnticipatedTransition> getAnticipatedTransitionByComponent(Component component);
}
import java.util.Collection;
import java.util.List;
import org.sonar.api.rule.RuleKey;
+import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.core.issue.AnticipatedTransition;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
}
@Override
- public Collection<AnticipatedTransition> getAnticipatedTransitionByProjectUuid(String componentUuid, String filePath) {
+ public Collection<AnticipatedTransition> getAnticipatedTransitionByComponent(Component component) {
try (DbSession dbSession = dbClient.openSession(false)) {
- EntityDto entityDto = dbClient.entityDao().selectByComponentUuid(dbSession, componentUuid).orElseThrow(IllegalStateException::new);
- List<AnticipatedTransitionDto> anticipatedTransitionDtos = dbClient.anticipatedTransitionDao().selectByProjectUuid(dbSession, entityDto.getUuid());
+ String projectUuid = dbClient.entityDao().selectByComponentUuid(dbSession, component.getUuid()).map(EntityDto::getUuid)
+ .orElse(calculateProjectUuidFromComponentKey(dbSession, component));
+ List<AnticipatedTransitionDto> anticipatedTransitionDtos = dbClient.anticipatedTransitionDao()
+ .selectByProjectUuidAndFilePath(dbSession, projectUuid, component.getName());
return getAnticipatedTransitions(anticipatedTransitionDtos);
}
}
+ private String calculateProjectUuidFromComponentKey(DbSession dbSession, Component component) {
+ String projectKey = component.getKey().split(":")[0];
+ return dbClient.projectDao().selectProjectByKey(dbSession, projectKey).map(EntityDto::getUuid).orElse("");
+ }
+
private Collection<AnticipatedTransition> getAnticipatedTransitions(List<AnticipatedTransitionDto> anticipatedTransitionDtos) {
return anticipatedTransitionDtos
.stream()
transitionDto.getUserUuid(),
RuleKey.parse(transitionDto.getRuleKey()),
transitionDto.getMessage(),
- "filepath",
+ transitionDto.getFilePath(),
transitionDto.getLine(),
transitionDto.getLineHash(),
transitionDto.getTransition(),
@Override
public void beforeComponent(Component component) {
if (FILE.equals(component.getType())) {
- anticipatedTransitions = anticipatedTransitionRepository.getAnticipatedTransitionByProjectUuid(component.getUuid(), component.getName());
+ anticipatedTransitions = anticipatedTransitionRepository.getAnticipatedTransitionByComponent(component);
}
}
}
}
- @Override
- public void afterComponent(Component component) {
- anticipatedTransitions.clear();
- }
-
private void performAnticipatedTransition(DefaultIssue issue, AnticipatedTransition anticipatedTransition) {
issue.setBeingClosed(true);
issue.setAnticipatedTransitions(true);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.ce.task.projectanalysis.issue;
+
+import java.util.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.ComponentImpl;
+import org.sonar.ce.task.projectanalysis.component.ReportAttributes;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.AnticipatedTransitionDto;
+import org.sonar.db.project.ProjectDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
+
+public class AnticipatedTransitionRepositoryImplTest {
+
+ @Rule
+ public final DbTester db = DbTester.create(System2.INSTANCE);
+
+ private final DbClient dbClient = db.getDbClient();
+
+ private final AnticipatedTransitionRepository underTest = new AnticipatedTransitionRepositoryImpl(dbClient);
+
+ @Before
+ public void cleanUpDb() {
+
+ }
+
+ @Test
+ public void giveAnticipatedTransitionsForFile_shouldBeReturnedCorrectly() {
+ //given
+ String projectKey = "projectKey1";
+ String projectUuid = "projectUuid1";
+ dbClient.projectDao().insert(db.getSession(), getProjectDto(projectUuid, projectKey));
+
+ insertAnticipatedTransition(projectUuid, "file1.js");
+ insertAnticipatedTransition(projectUuid, "file2.js");
+ insertAnticipatedTransition(projectUuid, "file2.js");
+
+ db.getSession().commit();
+
+ String componentUuid = "componentUuid";
+ Component file = getFileComponent(componentUuid, projectKey, "file1.js");
+ var anticipatedTransitions = underTest.getAnticipatedTransitionByComponent(file);
+ assertThat(anticipatedTransitions).hasSize(1);
+
+ file = getFileComponent(componentUuid, projectKey, "file2.js");
+ anticipatedTransitions = underTest.getAnticipatedTransitionByComponent(file);
+ assertThat(anticipatedTransitions).hasSize(2);
+
+ file = getFileComponent(componentUuid, projectKey, "file3.js");
+ anticipatedTransitions = underTest.getAnticipatedTransitionByComponent(file);
+ assertThat(anticipatedTransitions).isEmpty();
+ }
+
+ @Test
+ public void giveProjectBranchAvailable_projectUuidShouldBeCalculatedFromThere() {
+ //given
+ String projectKey = "projectKey2";
+ String projectUuid = "projectUuid2";
+ String mainFile = "file1.js";
+ dbClient.projectDao().insert(db.getSession(), getProjectDto(projectUuid, projectKey));
+
+ BranchDto branchDto = getBranchDto(projectUuid, "branch");
+ dbClient.branchDao().insert(db.getSession(), branchDto);
+
+ ComponentDto fileDto = getComponentDto(projectKey + ":" + mainFile, branchDto.getUuid());
+ dbClient.componentDao().insertOnMainBranch(db.getSession(), fileDto);
+
+ insertAnticipatedTransition(projectUuid, mainFile);
+ insertAnticipatedTransition(projectUuid, "file2.js");
+ insertAnticipatedTransition(projectUuid, "file2.js");
+
+ db.getSession().commit();
+
+ Component file = getFileComponent(fileDto.uuid(), projectKey, mainFile);
+ var anticipatedTransitions = underTest.getAnticipatedTransitionByComponent(file);
+ assertThat(anticipatedTransitions).hasSize(1);
+ }
+
+ private void insertAnticipatedTransition(String projectUuid, String filename) {
+ var anticipatedTransition = getAnticipatedTransition(projectUuid, filename);
+ dbClient.anticipatedTransitionDao().insert(db.getSession(), anticipatedTransition);
+ }
+
+ private ComponentDto getComponentDto(String componentKey, String branchUuid) {
+ ComponentDto componentDto = new ComponentDto();
+ componentDto.setQualifier("FIL")
+ .setUuid(Uuids.createFast())
+ .setKey(componentKey)
+ .setBranchUuid(branchUuid)
+ .setUuidPath(Uuids.createFast());
+ return componentDto;
+ }
+
+
+ private BranchDto getBranchDto(String projectUuid, String key) {
+ BranchDto branchDto = new BranchDto();
+ branchDto.setProjectUuid(projectUuid)
+ .setUuid(Uuids.createFast())
+ .setIsMain(true)
+ .setBranchType(BranchType.BRANCH)
+ .setKey(key);
+ return branchDto;
+ }
+
+ private ProjectDto getProjectDto(String projectUuid, String projectKey) {
+ ProjectDto projectDto = new ProjectDto();
+ projectDto.setKey(projectKey);
+ projectDto.setUuid(projectUuid);
+ projectDto.setQualifier("TRK");
+ projectDto.setName("project");
+ return projectDto;
+ }
+
+ private Component getFileComponent(String componenUuid, String projectKey, String filename) {
+ return ComponentImpl.builder(FILE)
+ .setUuid(componenUuid)
+ .setKey(String.format("%s:%s", projectKey, filename))
+ .setName(filename)
+ .setStatus(Component.Status.ADDED)
+ .setShortName(filename)
+ .setReportAttributes(mock(ReportAttributes.class)).build();
+ }
+
+ private AnticipatedTransitionDto getAnticipatedTransition(String projectUuid, String filename) {
+ return new AnticipatedTransitionDto(Uuids.createFast(), projectUuid, "admin", "wontfix", null, null, null, null, "rule:key", filename, (new Date()).getTime());
+ }
+
+}
underTest.beforeComponent(component);
underTest.onIssue(component, issue);
- underTest.afterComponent(component);
assertThat(issue.isBeingClosed()).isTrue();
assertThat(issue.hasAnticipatedTransitions()).isTrue();
underTest.beforeComponent(component);
underTest.onIssue(component, issue);
- underTest.afterComponent(component);
assertThat(issue.isBeingClosed()).isFalse();
assertThat(issue.hasAnticipatedTransitions()).isFalse();
*/
package org.sonar.db.issue;
+import java.time.Instant;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
public void select_anticipated_transition() {
final String projectUuid = "project147852";
String atUuid = "uuid_123456";
- AnticipatedTransitionDto transition = new AnticipatedTransitionDto(
- atUuid,
- projectUuid,
- "userUuid",
- "transition",
- "status",
- "comment",
- 1,
- "message",
- "lineHash",
- "ruleKey");
-
+ String atUuid2 = "uuid_123457";
// insert one
- underTest.insert(db.getSession(), transition);
+ generateAndInsertAnticipatedTransition(atUuid, projectUuid, "userUuid", "filePath1");
+ generateAndInsertAnticipatedTransition(atUuid2, projectUuid, "userUuid", "filePath2");
// select all
var anticipatedTransitionDtos = underTest.selectByProjectUuid(db.getSession(), projectUuid);
- assertThat(anticipatedTransitionDtos).hasSize(1);
+ assertThat(anticipatedTransitionDtos).hasSize(2);
assertThat(anticipatedTransitionDtos.get(0))
.extracting("uuid").isEqualTo(atUuid);
+ assertThat(anticipatedTransitionDtos.get(1))
+ .extracting("uuid").isEqualTo(atUuid2);
// delete one
underTest.delete(db.getSession(), atUuid);
// select all
var anticipatedTransitionDtosDeleted = underTest.selectByProjectUuid(db.getSession(), projectUuid);
- assertThat(anticipatedTransitionDtosDeleted).isEmpty();
+ assertThat(anticipatedTransitionDtosDeleted).hasSize(1);
+ }
+
+ @Test
+ public void select_anticipated_transition_by_project_and_filepath() {
+ final String projectUuid = "project147852";
+ String atUuid = "uuid_123456";
+ String atUuid2 = "uuid_123457";
+ String filePath = "filePath1";
+
+ // insert two
+ generateAndInsertAnticipatedTransition(atUuid, projectUuid, "userUuid", filePath);
+ generateAndInsertAnticipatedTransition(atUuid2, projectUuid, "userUuid", "filePath2");
+
+ // select one by project filePath
+ var anticipatedTransitionDtos = underTest.selectByProjectUuidAndFilePath(db.getSession(), projectUuid, filePath);
+ assertThat(anticipatedTransitionDtos).hasSize(1);
+ assertThat(anticipatedTransitionDtos.get(0))
+ .extracting("uuid", "filePath").containsExactly(atUuid, filePath);
+
+ // delete one
+ underTest.delete(db.getSession(), atUuid);
+
+ // select all
+ var anticipatedTransitionDtosDeleted = underTest.selectByProjectUuid(db.getSession(), projectUuid);
+ assertThat(anticipatedTransitionDtosDeleted).hasSize(1);
}
@Test
assertThat(underTest.selectByProjectUuid(db.getSession(), projectUuid2)).hasSize(2);
}
- private void generateAndInsertAnticipatedTransition(String uuid, String projectUuid1, String userUuid1) {
- AnticipatedTransitionDto transition = new AnticipatedTransitionDto(
+ private void generateAndInsertAnticipatedTransition(String uuid, String projectUuid, String userUuid) {
+ generateAndInsertAnticipatedTransition(uuid, projectUuid, userUuid, "filePath");
+ }
+
+ private void generateAndInsertAnticipatedTransition(String uuid, String projectUuid, String userUuid, String filePath) {
+ AnticipatedTransitionDto transition = new AnticipatedTransitionDto(
uuid,
- projectUuid1,
- userUuid1,
+ projectUuid,
+ userUuid,
"transition",
"status",
- "comment",
1,
"message",
"lineHash",
- "ruleKey");
+ "ruleKey",
+ filePath,
+ Instant.now().getEpochSecond());
// insert one
underTest.insert(db.getSession(), transition);
return mapper(session).selectByProjectUuid(projectUuid);
}
+ public List<AnticipatedTransitionDto> selectByProjectUuidAndFilePath(DbSession session, String projectUuid, String filePath) {
+ return mapper(session).selectByProjectUuidAndFilePath(projectUuid, filePath);
+ }
+
private static AnticipatedTransitionMapper mapper(DbSession session) {
return session.getMapper(AnticipatedTransitionMapper.class);
}
private String projectUuid;
private String userUuid;
private String transition;
- private String status;
private String comment;
private Integer line;
private String message;
private String lineHash;
private String ruleKey;
- // TODO: private String filePath
- // TODO: private Instant createdAt
+ private String filePath;
+ private Long createdAt;
public AnticipatedTransitionDto(
String projectUuid,
String userUuid,
String transition,
- String status,
@Nullable String comment,
@Nullable Integer line,
@Nullable String message,
@Nullable String lineHash,
- String ruleKey) {
+ String ruleKey, String filePath, Long createdAt) {
this.uuid = uuid;
this.projectUuid = projectUuid;
this.userUuid = userUuid;
this.transition = transition;
- this.status = status;
this.comment = comment;
this.line = line;
this.message = message;
this.lineHash = lineHash;
this.ruleKey = ruleKey;
+ this.filePath = filePath;
+ this.createdAt = createdAt;
}
public String getUuid() {
this.transition = transition;
}
- public String getStatus() {
- return status;
- }
-
- public void setStatus(String status) {
- this.status = status;
- }
-
public String getComment() {
return comment;
}
this.ruleKey = ruleKey;
}
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public Long getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Long createdAt) {
+ this.createdAt = createdAt;
+ }
+
public static AnticipatedTransitionDto toDto(AnticipatedTransition anticipatedTransition, String uuid, String projectUuid) {
return new AnticipatedTransitionDto(
uuid,
projectUuid,
anticipatedTransition.getUserUuid(),
anticipatedTransition.getTransition(),
- anticipatedTransition.getStatus(),
anticipatedTransition.getComment(),
anticipatedTransition.getLine(),
anticipatedTransition.getMessage(),
anticipatedTransition.getLineHash(),
- anticipatedTransition.getRuleKey().toString());
+ anticipatedTransition.getRuleKey().toString(),
+ anticipatedTransition.getFilePath(),
+ anticipatedTransition.getUpdateDate().getTime());
}
}
void deleteByProjectAndUser(@Param("projectUuid") String projectUuid, @Param("userUuid") String userUuid);
List<AnticipatedTransitionDto> selectByProjectUuid(@Param("projectUuid") String projectUuid);
+
+ List<AnticipatedTransitionDto> selectByProjectUuidAndFilePath(@Param("projectUuid") String projectUuid, @Param("filePath") String filePath);
}
at.project_uuid as projectUuid,
at.user_uuid as userUuid,
at.transition as transition,
- at.status as status,
at.transition_comment as "comment",
at.line as line,
at.message as message,
at.line_hash as lineHash,
- at.rule_key as ruleKey
+ at.rule_key as ruleKey,
+ at.file_path as filePath,
+ at.created_at as createdAt
</sql>
<insert id="insert" useGeneratedKeys="false" parameterType="AnticipatedTransition">
- INSERT INTO anticipated_transitions (uuid, project_uuid, user_uuid, transition, status,
- transition_comment, line, message, line_hash, rule_key)
+ INSERT INTO anticipated_transitions (uuid, project_uuid, user_uuid, transition,
+ transition_comment, line, message, line_hash, rule_key, file_path, created_at)
VALUES (#{uuid,jdbcType=VARCHAR}, #{projectUuid,jdbcType=VARCHAR}, #{userUuid,jdbcType=VARCHAR},
- #{transition,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{comment,jdbcType=VARCHAR},
- #{line,jdbcType=INTEGER}, #{message,jdbcType=VARCHAR}, #{lineHash,jdbcType=VARCHAR}, #{ruleKey,jdbcType=VARCHAR})
+ #{transition,jdbcType=VARCHAR}, #{comment,jdbcType=VARCHAR},
+ #{line,jdbcType=INTEGER}, #{message,jdbcType=VARCHAR}, #{lineHash,jdbcType=VARCHAR}, #{ruleKey,jdbcType=VARCHAR},
+ #{filePath,jdbcType=VARCHAR}, #{createdAt,jdbcType=BIGINT})
</insert>
<delete id="delete" parameterType="string">
from anticipated_transitions at
where at.project_uuid=#{projectUuid,jdbcType=VARCHAR}
</select>
+
+ <select id="selectByProjectUuidAndFilePath" parameterType="string" resultType="AnticipatedTransition">
+ select
+ <include refid="anticipatedTransitionsColumns"/>
+ from anticipated_transitions at
+ where at.project_uuid=#{projectUuid,jdbcType=VARCHAR} and at.file_path=#{filePath,jdbcType=VARCHAR}
+ </select>
+
</mapper>
"PROJECT_UUID" CHARACTER VARYING(40) NOT NULL,
"USER_UUID" CHARACTER VARYING(255) NOT NULL,
"TRANSITION" CHARACTER VARYING(20) NOT NULL,
- "STATUS" CHARACTER VARYING(20) NOT NULL,
"TRANSITION_COMMENT" CHARACTER VARYING(4000),
"LINE" INTEGER,
"MESSAGE" CHARACTER VARYING(4000),
"LINE_HASH" CHARACTER VARYING(255),
- "RULE_KEY" CHARACTER VARYING(200) NOT NULL
+ "RULE_KEY" CHARACTER VARYING(200) NOT NULL,
+ "FILE_PATH" CHARACTER VARYING(1500) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
);
ALTER TABLE "ANTICIPATED_TRANSITIONS" ADD CONSTRAINT "PK_ANTICIPATED_TRANSITIONS" PRIMARY KEY("UUID");
import org.sonar.server.platform.db.migration.step.CreateTableChange;
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.IntegerColumnDef.newIntegerColumnDefBuilder;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.MAX_SIZE;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.USER_UUID_SIZE;
.addColumn(newVarcharColumnDefBuilder().setColumnName("project_uuid").setIsNullable(false).setLimit(UUID_SIZE).build())
.addColumn(newVarcharColumnDefBuilder().setColumnName("user_uuid").setIsNullable(false).setLimit(USER_UUID_SIZE).build())
.addColumn(newVarcharColumnDefBuilder().setColumnName("transition").setIsNullable(false).setLimit(20).build())
- .addColumn(newVarcharColumnDefBuilder().setColumnName("status").setIsNullable(false).setLimit(20).build())
.addColumn(newVarcharColumnDefBuilder().setColumnName("transition_comment").setLimit(MAX_SIZE).build())
.addColumn(newIntegerColumnDefBuilder().setColumnName("line").build())
.addColumn(newVarcharColumnDefBuilder().setColumnName("message").setLimit(MAX_SIZE).build())
.addColumn(newVarcharColumnDefBuilder().setColumnName("line_hash").setLimit(255).build())
.addColumn(newVarcharColumnDefBuilder().setColumnName("rule_key").setIsNullable(false).setLimit(200).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("file_path").setIsNullable(false).setLimit(1500).build())
+ .addColumn(newBigIntegerColumnDefBuilder().setColumnName("created_at").setIsNullable(false).build())
.build());
}
}
db.assertColumnDefinition(ANTICIPATED_TRANSITIONS_TABLE_NAME, "project_uuid", Types.VARCHAR, UUID_SIZE, false);
db.assertColumnDefinition(ANTICIPATED_TRANSITIONS_TABLE_NAME, "user_uuid", Types.VARCHAR, USER_UUID_SIZE, false);
db.assertColumnDefinition(ANTICIPATED_TRANSITIONS_TABLE_NAME, "transition", Types.VARCHAR, 20, false);
- db.assertColumnDefinition(ANTICIPATED_TRANSITIONS_TABLE_NAME, "status", Types.VARCHAR, 20, false);
db.assertColumnDefinition(ANTICIPATED_TRANSITIONS_TABLE_NAME, "transition_comment", Types.VARCHAR, MAX_SIZE, true);
db.assertColumnDefinition(ANTICIPATED_TRANSITIONS_TABLE_NAME, "line", Types.INTEGER, 11, true);
db.assertColumnDefinition(ANTICIPATED_TRANSITIONS_TABLE_NAME, "message", Types.VARCHAR, MAX_SIZE, true);