public class GitlabHttpClientTest {
-
@Rule
public LogTester logTester = new LogTester();
}
@Test
- public void get_branches(){
+ public void get_branches() {
MockResponse response = new MockResponse()
.setResponseCode(200)
.setBody("[{\n"
assertThat(projectList.getProjects()).hasSize(3);
assertThat(projectList.getProjects()).extracting(
Project::getId, Project::getName, Project::getNameWithNamespace, Project::getPath, Project::getPathWithNamespace, Project::getWebUrl).containsExactly(
- tuple(1L, "SonarQube example 1", "SonarSource / SonarQube / SonarQube example 1", "sonarqube-example-1", "sonarsource/sonarqube/sonarqube-example-1",
- "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-1"),
- tuple(2L, "SonarQube example 2", "SonarSource / SonarQube / SonarQube example 2", "sonarqube-example-2", "sonarsource/sonarqube/sonarqube-example-2",
- "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-2"),
- tuple(3L, "SonarQube example 3", "SonarSource / SonarQube / SonarQube example 3", "sonarqube-example-3", "sonarsource/sonarqube/sonarqube-example-3",
- "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-3"));
+ tuple(1L, "SonarQube example 1", "SonarSource / SonarQube / SonarQube example 1", "sonarqube-example-1", "sonarsource/sonarqube/sonarqube-example-1",
+ "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-1"),
+ tuple(2L, "SonarQube example 2", "SonarSource / SonarQube / SonarQube example 2", "sonarqube-example-2", "sonarsource/sonarqube/sonarqube-example-2",
+ "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-2"),
+ tuple(3L, "SonarQube example 3", "SonarSource / SonarQube / SonarQube example 3", "sonarqube-example-3", "sonarsource/sonarqube/sonarqube-example-3",
+ "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-3"));
RecordedRequest projectGitlabRequest = server.takeRequest(10, TimeUnit.SECONDS);
String gitlabUrlCall = projectGitlabRequest.getRequestUrl().toString();
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Could not validate GitLab read permission. Got an unexpected answer.");
assertThat(logTester.logs(LoggerLevel.INFO).get(0))
- .contains("Gitlab API call to [http://localhost:"+server.getPort()+"/projects] " +
- "failed with error message : [Failed to connect to localhost");
+ .contains("Gitlab API call to [" + server.url("/projects") + "] " +
+ "failed with error message : [Failed to connect to " + server.getHostName());
}
@Test
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Could not validate GitLab token. Got an unexpected answer.");
assertThat(logTester.logs(LoggerLevel.INFO).get(0))
- .contains("Gitlab API call to [http://localhost:"+server.getPort()+"/user] " +
- "failed with error message : [Failed to connect to localhost");
+ .contains("Gitlab API call to [" + server.url("user") + "] " +
+ "failed with error message : [Failed to connect to " + server.getHostName());
}
@Test
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Could not validate GitLab write permission. Got an unexpected answer.");
assertThat(logTester.logs(LoggerLevel.INFO).get(0))
- .contains("Gitlab API call to [http://localhost:"+server.getPort()+"/markdown] " +
- "failed with error message : [Failed to connect to localhost");
+ .contains("Gitlab API call to [" + server.url("/markdown") + "] " +
+ "failed with error message : [Failed to connect to " + server.getHostName());
}
@Test
assertThatThrownBy(() -> underTest.getProject(gitlabUrl, "token", 0L))
.isInstanceOf(IllegalStateException.class)
- .hasMessageContaining("Failed to connect to localhost");
+ .hasMessageContaining("Failed to connect to");
assertThat(logTester.logs(LoggerLevel.INFO).get(0))
- .contains("Gitlab API call to [http://localhost:"+server.getPort()+"/projects/0] " +
- "failed with error message : [Failed to connect to localhost");
+ .contains("Gitlab API call to [" + server.url("/projects/0") + "] " +
+ "failed with error message : [Failed to connect to " + server.getHostName());
}
@Test
assertThatThrownBy(() -> underTest.getBranches(gitlabUrl, "token", 0L))
.isInstanceOf(IllegalStateException.class)
- .hasMessageContaining("Failed to connect to localhost");
+ .hasMessageContaining("Failed to connect to " + server.getHostName());
assertThat(logTester.logs(LoggerLevel.INFO).get(0))
- .contains("Gitlab API call to [http://localhost:"+server.getPort()+"/projects/0/repository/branches] " +
- "failed with error message : [Failed to connect to localhost");
+ .contains("Gitlab API call to [" + server.url("/projects/0/repository/branches") + "] " +
+ "failed with error message : [Failed to connect to " + server.getHostName());
}
@Test
assertThatThrownBy(() -> underTest.searchProjects(gitlabUrl, "token", null, 1, 1))
.isInstanceOf(IllegalStateException.class)
- .hasMessageContaining("Failed to connect to localhost");
+ .hasMessageContaining("Failed to connect to");
assertThat(logTester.logs(LoggerLevel.INFO).get(0))
- .contains("Gitlab API call to [http://localhost:"+server.getPort()+"/projects?archived=false&simple=true&membership=true&order_by=name&sort=asc&search=&page=1&per_page=1] " +
- "failed with error message : [Failed to connect to localhost");
+ .contains(
+ "Gitlab API call to [" + server.url("/projects?archived=false&simple=true&membership=true&order_by=name&sort=asc&search=&page=1&per_page=1")
+ + "] " +
+ "failed with error message : [Failed to connect to " + server.getHostName());
}
}
import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey;
/**
- * Cache a map of component key -> set<uuid> in sibling PRs that have open issues
+ * Cache a map of component key -> set<uuid> in:
+ * - sibling PRs that have open issues
+ * - branches that use reference branch as new code period setting and have it set to currently analysed branch
*/
public class SiblingComponentsWithOpenIssues {
private final DbClient dbClient;
String currentBranchUuid = treeRootHolder.getRoot().getUuid();
String referenceBranchUuid;
- if (metadataHolder.isPullRequest()) {
- referenceBranchUuid = metadataHolder.getBranch().getReferenceBranchUuid();
- } else {
- referenceBranchUuid = currentBranchUuid;
- }
-
uuidsByKey = new HashMap<>();
+
try (DbSession dbSession = dbClient.openSession(false)) {
- List<KeyWithUuidDto> components = dbClient.componentDao().selectAllSiblingComponentKeysHavingOpenIssues(dbSession, referenceBranchUuid, currentBranchUuid);
- for (KeyWithUuidDto dto : components) {
- uuidsByKey.computeIfAbsent(removeBranchAndPullRequestFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid());
+ if (metadataHolder.isPullRequest()) {
+ referenceBranchUuid = metadataHolder.getBranch().getReferenceBranchUuid();
+ } else {
+ referenceBranchUuid = currentBranchUuid;
+ addComponentsFromBranchesThatUseCurrentBranchAsNewCodePeriodReferenceAndHaveOpenIssues(dbSession);
}
+
+ addComponentsFromPullRequestsTargetingCurrentBranchThatHaveOpenIssues(dbSession, referenceBranchUuid, currentBranchUuid);
+ }
+ }
+
+ private void addComponentsFromBranchesThatUseCurrentBranchAsNewCodePeriodReferenceAndHaveOpenIssues(DbSession dbSession) {
+ String projectUuid = metadataHolder.getProject().getUuid();
+ String currentBranchName = metadataHolder.getBranch().getName();
+
+ Set<String> branchUuids = dbClient.newCodePeriodDao().selectBranchesReferencing(dbSession, projectUuid, currentBranchName);
+
+ List<KeyWithUuidDto> components = dbClient.componentDao().selectComponentsFromBranchesThatHaveOpenIssues(dbSession, branchUuids);
+ for (KeyWithUuidDto dto : components) {
+ uuidsByKey.computeIfAbsent(removeBranchAndPullRequestFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid());
+ }
+ }
+
+ private void addComponentsFromPullRequestsTargetingCurrentBranchThatHaveOpenIssues(DbSession dbSession, String referenceBranchUuid, String currentBranchUuid) {
+ List<KeyWithUuidDto> components = dbClient.componentDao().selectComponentsFromPullRequestsTargetingCurrentBranchThatHaveOpenIssues(
+ dbSession, referenceBranchUuid, currentBranchUuid);
+ for (KeyWithUuidDto dto : components) {
+ uuidsByKey.computeIfAbsent(removeBranchAndPullRequestFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid());
}
}
import org.sonar.core.issue.FieldDiffs;
import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.util.Uuids;
+import org.sonar.db.component.BranchType;
import org.sonar.server.issue.IssueFieldsSetter;
import org.sonar.server.issue.workflow.IssueWorkflow;
this(analysisMetadataHolder, IssueChangeContext.createScan(new Date(analysisMetadataHolder.getAnalysisDate())), workflow, updater, debtCalculator, ruleRepository);
}
- @VisibleForTesting
- IssueLifecycle(AnalysisMetadataHolder analysisMetadataHolder, IssueChangeContext changeContext, IssueWorkflow workflow, IssueFieldsSetter updater,
+ @VisibleForTesting IssueLifecycle(AnalysisMetadataHolder analysisMetadataHolder, IssueChangeContext changeContext, IssueWorkflow workflow, IssueFieldsSetter updater,
DebtCalculator debtCalculator, RuleRepository ruleRepository) {
this.analysisMetadataHolder = analysisMetadataHolder;
this.workflow = workflow;
raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_BRANCH, branchName, analysisMetadataHolder.getBranch().getName());
}
- public void mergeConfirmedOrResolvedFromPr(DefaultIssue raw, DefaultIssue base, String pr) {
+ public void mergeConfirmedOrResolvedFromPrOrBranch(DefaultIssue raw, DefaultIssue base, BranchType branchType, String prOrBranchKey) {
copyAttributesOfIssueFromAnotherBranch(raw, base);
- String from = "#" + pr;
+ String from = (branchType == BranchType.PULL_REQUEST) ? "#" + prOrBranchKey : prOrBranchKey;
String to = analysisMetadataHolder.isPullRequest() ? ("#" + analysisMetadataHolder.getPullRequestKey()) : analysisMetadataHolder.getBranch().getName();
raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_BRANCH, from, to);
}
import javax.annotation.concurrent.Immutable;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.issue.tracking.Trackable;
+import org.sonar.db.component.BranchType;
@Immutable
public class SiblingIssue implements Trackable {
private final RuleKey ruleKey;
private final String status;
private final String prKey;
+ private final BranchType branchType;
private final Date updateDate;
- SiblingIssue(String key, @Nullable Integer line, @Nullable String message, @Nullable String lineHash, RuleKey ruleKey, String status, String prKey, Date updateDate) {
+ SiblingIssue(String key, @Nullable Integer line, @Nullable String message, @Nullable String lineHash, RuleKey ruleKey, String status, String prKey, BranchType branchType,
+ Date updateDate) {
this.key = key;
this.line = line;
this.message = message;
this.ruleKey = ruleKey;
this.status = status;
this.prKey = prKey;
+ this.branchType = branchType;
this.updateDate = updateDate;
}
return message;
}
+ public BranchType getBranchType() {
+ return branchType;
+ }
+
@CheckForNull
@Override
public String getLineHash() {
for (Map.Entry<DefaultIssue, SiblingIssue> e : matchedRaws.entrySet()) {
SiblingIssue issue = e.getValue();
- issueLifecycle.mergeConfirmedOrResolvedFromPr(e.getKey(), defaultIssues.get(issue), issue.getPrKey());
+ issueLifecycle.mergeConfirmedOrResolvedFromPrOrBranch(e.getKey(), defaultIssues.get(issue), issue.getBranchType(), issue.getPrKey());
}
}
}
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
-import org.sonar.api.utils.Preconditions;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
-import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.issue.PrIssueDto;
}
private static SiblingIssue toSiblingIssue(PrIssueDto dto) {
- Preconditions.checkState(dto.getBranchType().equals(BranchType.PULL_REQUEST), "Expected all issues to belong to P/Rs");
return new SiblingIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus(), dto.getBranchKey(),
- longToDate(dto.getIssueUpdateDate()));
+ dto.getBranchType(), longToDate(dto.getIssueUpdateDate()));
}
public Map<SiblingIssue, DefaultIssue> loadDefaultIssuesWithChanges(Collection<SiblingIssue> lightIssues) {
*/
package org.sonar.ce.task.projectanalysis.component;
+import java.util.Collections;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.issue.IssueTesting;
import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.server.project.Project;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@Before
public void setUp() {
ComponentDto project = db.components().insertPublicProject();
+ metadataHolder.setProject(new Project(project.uuid(), project.getKey(), project.name(), project.description(), Collections.emptyList()));
branch1 = db.components().insertProjectBranch(project, b -> b.setKey("branch1"), b -> b.setBranchType(BranchType.BRANCH));
branch1pr1 = db.components().insertProjectBranch(project,
when(branch.getName()).thenReturn("master");
analysisMetadataHolder.setBranch(branch);
- underTest.mergeConfirmedOrResolvedFromPr(raw, fromShort, "2");
+ underTest.mergeConfirmedOrResolvedFromPrOrBranch(raw, fromShort, BranchType.PULL_REQUEST, "2");
assertThat(raw.resolution()).isEqualTo("resolution");
assertThat(raw.status()).isEqualTo("status");
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.db.issue.IssueTesting;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
+import org.sonar.server.project.Project;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.db.component.ComponentTesting.newFileDto;
public class SiblingsIssueMergerTest {
- @Mock
- private IssueLifecycle issueLifecycle;
-
- @Mock
- private Branch branch;
+ private final IssueLifecycle issueLifecycle = mock(IssueLifecycle.class);
+ private final Branch branch = mock(Branch.class);
@Rule
public DbTester db = DbTester.create();
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
DbClient dbClient = db.getDbClient();
ComponentIssuesLoader componentIssuesLoader = new ComponentIssuesLoader(dbClient, null, null, new MapSettings().asConfig(), System2.INSTANCE);
copier = new SiblingsIssueMerger(new SiblingsIssuesLoader(new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, dbClient), dbClient, componentIssuesLoader),
rule = db.rules().insert();
when(branch.getReferenceBranchUuid()).thenReturn(projectDto.uuid());
metadataHolder.setBranch(branch);
+ metadataHolder.setProject(new Project(projectDto.uuid(), projectDto.getKey(), projectDto.name(), projectDto.description(), Collections.emptyList()));
}
@Test
copier.tryMerge(FILE_1, Collections.singleton(newIssue));
ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(issueLifecycle).mergeConfirmedOrResolvedFromPr(eq(newIssue), issueToMerge.capture(), eq("myBranch1"));
+ verify(issueLifecycle).mergeConfirmedOrResolvedFromPrOrBranch(eq(newIssue), issueToMerge.capture(), eq(BranchType.PULL_REQUEST), eq("myBranch1"));
assertThat(issueToMerge.getValue().key()).isEqualTo("issue1");
}
copier.tryMerge(FILE_1, Collections.singleton(newIssue));
ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(issueLifecycle).mergeConfirmedOrResolvedFromPr(eq(newIssue), issueToMerge.capture(), eq("myBranch1"));
+ verify(issueLifecycle).mergeConfirmedOrResolvedFromPrOrBranch(eq(newIssue), issueToMerge.capture(), eq(BranchType.PULL_REQUEST), eq("myBranch1"));
assertThat(issueToMerge.getValue().key()).isEqualTo("issue1");
}
copier.tryMerge(FILE_1, Collections.singleton(newIssue));
ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(issueLifecycle).mergeConfirmedOrResolvedFromPr(eq(newIssue), issueToMerge.capture(), eq("myBranch2"));
+ verify(issueLifecycle).mergeConfirmedOrResolvedFromPrOrBranch(eq(newIssue), issueToMerge.capture(), eq(BranchType.PULL_REQUEST), eq("myBranch2"));
assertThat(issueToMerge.getValue().key()).isEqualTo("issue");
assertThat(issueToMerge.getValue().defaultIssueComments()).isNotEmpty();
* Returns components with open issues from P/Rs that use a certain branch as reference (reference branch).
* Excludes components from the current branch.
*/
- public List<KeyWithUuidDto> selectAllSiblingComponentKeysHavingOpenIssues(DbSession dbSession, String referenceBranchUuid, String currentBranchUuid) {
- return mapper(dbSession).selectAllSiblingComponentKeysHavingOpenIssues(referenceBranchUuid, currentBranchUuid);
+ public List<KeyWithUuidDto> selectComponentsFromPullRequestsTargetingCurrentBranchThatHaveOpenIssues(DbSession dbSession, String referenceBranchUuid, String currentBranchUuid) {
+ return mapper(dbSession).selectComponentsFromPullRequestsTargetingCurrentBranchThatHaveOpenIssues(referenceBranchUuid, currentBranchUuid);
+ }
+
+ /**
+ * Returns components with open issues from the given branches
+ */
+ public List<KeyWithUuidDto> selectComponentsFromBranchesThatHaveOpenIssues(DbSession dbSession, Set<String> branchUuids) {
+ if (branchUuids.isEmpty()) {
+ return emptyList();
+ }
+
+ return executeLargeInputs(branchUuids, input -> mapper(dbSession).selectComponentsFromBranchesThatHaveOpenIssues(input));
}
/**
void delete(String componentUuid);
- List<KeyWithUuidDto> selectAllSiblingComponentKeysHavingOpenIssues(@Param("referenceBranchUuid") String referenceBranchUuid,
- @Param("currentBranchUuid") String currentBranchUuid);
+ List<KeyWithUuidDto> selectComponentsFromPullRequestsTargetingCurrentBranchThatHaveOpenIssues(@Param("referenceBranchUuid") String referenceBranchUuid,
+ @Param("currentBranchUuid") String currentBranchUuid);
+
+ List<KeyWithUuidDto> selectComponentsFromBranchesThatHaveOpenIssues(@Param("branchUuids") List<String> branchUuids);
List<ProjectNclocDistributionDto> selectPrivateProjectsWithNcloc();
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
return ofNullable(mapper(dbSession).selectByBranch(projectUuid, branchUuid));
}
+ public Set<String> selectBranchesReferencing(DbSession dbSession, String projectUuid, String referenceBranchName) {
+ return mapper(dbSession).selectBranchesReferencing(projectUuid, referenceBranchName);
+ }
+
public boolean existsByProjectAnalysisUuid(DbSession dbSession, String projectAnalysisUuid) {
requireNonNull(projectAnalysisUuid, MSG_PROJECT_UUID_NOT_SPECIFIED);
return mapper(dbSession).countByProjectAnalysis(projectAnalysisUuid) > 0;
return this;
}
+ @CheckForNull
public String getValue() {
return value;
}
- public NewCodePeriodDto setValue(String value) {
+ public NewCodePeriodDto setValue(@Nullable String value) {
this.value = value;
return this;
}
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import org.apache.ibatis.annotations.Param;
public interface NewCodePeriodMapper {
NewCodePeriodDto selectByBranch(@Param("projectUuid") String projectUuid, @Param("branchUuid") String branchUuid);
+ Set<String> selectBranchesReferencing(@Param("projectUuid") String projectUuid, @Param("referenceBranchName") String referenceBranchName);
+
long countByProjectAnalysis(String projectAnalysisUuid);
List<NewCodePeriodDto> selectAllByProject(String projectUuid);
DELETE FROM components WHERE uuid=#{componentUuid,jdbcType=VARCHAR}
</delete>
- <select id="selectAllSiblingComponentKeysHavingOpenIssues" resultType="KeyWithUuid">
+ <select id="selectComponentsFromPullRequestsTargetingCurrentBranchThatHaveOpenIssues" resultType="KeyWithUuid">
SELECT DISTINCT p.kee as kee, p.uuid as uuid FROM components p
JOIN issues i
ON p.uuid = i.component_uuid
AND i.status != 'CLOSED'
</select>
+ <select id="selectComponentsFromBranchesThatHaveOpenIssues" resultType="KeyWithUuid">
+ SELECT DISTINCT p.kee as kee, p.uuid as uuid
+ FROM components p
+ JOIN issues i
+ ON p.uuid = i.component_uuid
+ JOIN project_branches b
+ ON i.project_uuid = b.uuid
+ AND b.uuid in <foreach collection="branchUuids" open="(" close=")" item="branchUuid" separator=",">
+ #{branchUuid,jdbcType=VARCHAR}
+ </foreach>
+ AND i.status != 'CLOSED'
+ </select>
+
<select id="selectPrivateProjectsWithNcloc" resultType="org.sonar.db.component.ProjectNclocDistributionDto">
select p.kee as kee, p.name as name, max(lm.value) as ncloc
from live_measures lm
</choose>
</update>
+ <select id="selectBranchesReferencing" parameterType="map" resultType="string">
+ <!-- branch setting -->
+ SELECT ncp.branch_uuid
+ FROM new_code_periods ncp
+ WHERE
+ ncp.branch_uuid IS NOT NULL
+ AND ncp.project_uuid = #{projectUuid, jdbcType=VARCHAR}
+ AND ncp.type='REFERENCE_BRANCH'
+ AND ncp.value=#{referenceBranchName, jdbcType=VARCHAR}
+ UNION
+
+ <!-- project default setting-->
+ SELECT pb.uuid
+ FROM project_branches pb, new_code_periods ncp1
+ WHERE
+ ncp1.project_uuid = pb.project_uuid
+ AND ncp1.branch_uuid IS NULL
+ AND ncp1.project_uuid = #{projectUuid, jdbcType=VARCHAR}
+ AND ncp1.type='REFERENCE_BRANCH'
+ AND ncp1.value=#{referenceBranchName, jdbcType=VARCHAR}
+ AND NOT EXISTS (select ncp2.value from new_code_periods ncp2 where ncp2.branch_uuid = pb.uuid)
+ AND pb.kee != #{referenceBranchName, jdbcType=VARCHAR}
+ </select>
+
+
<select id="selectByProject" parameterType="map" resultType="org.sonar.db.newcodeperiod.NewCodePeriodDto">
SELECT
<include refid="newCodePeriodMapperColumns"/>
);
ALTER TABLE "NEW_CODE_PERIODS" ADD CONSTRAINT "PK_NEW_CODE_PERIODS" PRIMARY KEY("UUID");
CREATE UNIQUE INDEX "UNIQ_NEW_CODE_PERIODS" ON "NEW_CODE_PERIODS"("PROJECT_UUID", "BRANCH_UUID");
+CREATE INDEX "IDX_NCP_TYPE" ON "NEW_CODE_PERIODS"("TYPE");
+CREATE INDEX "IDX_NCP_VALUE" ON "NEW_CODE_PERIODS"("VALUE");
CREATE TABLE "NOTIFICATIONS"(
"DATA" BLOB,
package org.sonar.db.component;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.RowNotFoundException;
+import org.sonar.db.issue.IssueDto;
import org.sonar.db.metric.MetricDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.source.FileSourceDto;
import static com.google.common.collect.ImmutableSet.of;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.entry;
import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.issue.Issue.STATUS_CLOSED;
+import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
+import static org.sonar.api.issue.Issue.STATUS_OPEN;
import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.SUBVIEW;
assertThat(underTest.existAnyOfComponentsWithQualifiers(db.getSession(), newHashSet(projectDto.getKey(), view.getKey()), newHashSet(APP, VIEW, SUBVIEW))).isTrue();
}
+ @Test
+ public void selectComponentsFromBranchesThatHaveOpenIssues() {
+ final ProjectDto project = db.components().insertPrivateProjectDto(b -> b.setName("foo"));
+
+ ComponentDto branch1 = db.components().insertProjectBranch(project, ComponentTesting.newBranchDto(project.getUuid(), BRANCH).setKey("branch1"));
+ ComponentDto fileBranch1 = db.components().insertComponent(ComponentTesting.newFileDto(branch1));
+
+ ComponentDto branch2 = db.components().insertProjectBranch(project, ComponentTesting.newBranchDto(project.getUuid(), BRANCH).setKey("branch2"));
+ ComponentDto fileBranch2 = db.components().insertComponent(ComponentTesting.newFileDto(branch2));
+ RuleDefinitionDto rule = db.rules().insert();
+ db.issues().insert(new IssueDto().setKee("i1").setComponent(fileBranch1).setProject(branch1).setRule(rule).setStatus(STATUS_CONFIRMED));
+ db.issues().insert(new IssueDto().setKee("i2").setComponent(fileBranch2).setProject(branch2).setRule(rule).setStatus(STATUS_CLOSED));
+ db.issues().insert(new IssueDto().setKee("i3").setComponent(fileBranch2).setProject(branch2).setRule(rule).setStatus(STATUS_OPEN));
+
+ List<KeyWithUuidDto> result = underTest.selectComponentsFromBranchesThatHaveOpenIssues(db.getSession(), of(branch1.uuid(), branch2.uuid()));
+
+ assertThat(result).extracting(KeyWithUuidDto::uuid).contains(fileBranch2.uuid());
+ }
+
+ @Test
+ public void selectComponentsFromBranchesThatHaveOpenIssues_returns_nothing_if_no_open_issues_in_sibling_branches() {
+ final ProjectDto project = db.components().insertPrivateProjectDto(b -> b.setName("foo"));
+ ComponentDto branch1 = db.components().insertProjectBranch(project, ComponentTesting.newBranchDto(project.getUuid(), BRANCH).setKey("branch1"));
+ ComponentDto fileBranch1 = db.components().insertComponent(ComponentTesting.newFileDto(branch1));
+ RuleDefinitionDto rule = db.rules().insert();
+ db.issues().insert(new IssueDto().setKee("i").setComponent(fileBranch1).setProject(branch1).setRule(rule).setStatus(STATUS_CLOSED));
+
+ List<KeyWithUuidDto> result = underTest.selectComponentsFromBranchesThatHaveOpenIssues(db.getSession(), singleton(branch1.uuid()));
+
+ assertThat(result).isEmpty();
+ }
+
private boolean privateFlagOfUuid(String uuid) {
return underTest.selectByUuid(db.getSession(), uuid).get().isPrivate();
}
return t -> {
};
}
-
}
package org.sonar.db.newcodeperiod;
import java.util.Optional;
+import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
+import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.project.ProjectDto;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS;
+import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION;
+import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
+import static org.sonar.db.newcodeperiod.NewCodePeriodType.SPECIFIC_ANALYSIS;
public class NewCodePeriodDaoTest {
public DbTester db = DbTester.create(System2.INSTANCE);
private final DbSession dbSession = db.getSession();
- private final UuidFactory uuidFactory = mock(UuidFactory.class);
+ private final UuidFactory uuidFactory = new SequenceUuidFactory();
private final NewCodePeriodDao underTest = new NewCodePeriodDao(System2.INSTANCE, uuidFactory);
-
- private static final String NEW_CODE_PERIOD_UUID = "ncp-uuid-1";
-
+
@Test
public void insert_new_code_period() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.NUMBER_OF_DAYS)
- .setValue("5"));
+ insert("proj-uuid", "branch-uuid", NUMBER_OF_DAYS, "5");
- Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, NEW_CODE_PERIOD_UUID);
+ Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, "1");
assertThat(resultOpt).isNotNull()
.isNotEmpty();
NewCodePeriodDto result = resultOpt.get();
- assertThat(result.getUuid()).isEqualTo(NEW_CODE_PERIOD_UUID);
+ assertThat(result.getUuid()).isEqualTo("1");
assertThat(result.getProjectUuid()).isEqualTo("proj-uuid");
assertThat(result.getBranchUuid()).isEqualTo("branch-uuid");
- assertThat(result.getType()).isEqualTo(NewCodePeriodType.NUMBER_OF_DAYS);
+ assertThat(result.getType()).isEqualTo(NUMBER_OF_DAYS);
assertThat(result.getValue()).isEqualTo("5");
assertThat(result.getCreatedAt()).isNotZero();
assertThat(result.getUpdatedAt()).isNotZero();
@Test
public void update_new_code_period() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.NUMBER_OF_DAYS)
- .setValue("5"));
+ insert("proj-uuid", "branch-uuid", NUMBER_OF_DAYS, "5");
underTest.update(dbSession, new NewCodePeriodDto()
- .setUuid(NEW_CODE_PERIOD_UUID)
- .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
+ .setUuid("1")
+ .setType(SPECIFIC_ANALYSIS)
.setProjectUuid("proj-uuid")
.setBranchUuid("branch-uuid")
.setValue("analysis-uuid"));
- Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, NEW_CODE_PERIOD_UUID);
+ Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, "1");
assertThat(resultOpt).isNotNull()
.isNotEmpty();
NewCodePeriodDto result = resultOpt.get();
- assertThat(result.getUuid()).isEqualTo(NEW_CODE_PERIOD_UUID);
+ assertThat(result.getUuid()).isEqualTo("1");
assertThat(result.getProjectUuid()).isEqualTo("proj-uuid");
assertThat(result.getBranchUuid()).isEqualTo("branch-uuid");
- assertThat(result.getType()).isEqualTo(NewCodePeriodType.SPECIFIC_ANALYSIS);
+ assertThat(result.getType()).isEqualTo(SPECIFIC_ANALYSIS);
assertThat(result.getValue()).isEqualTo("analysis-uuid");
assertThat(result.getCreatedAt()).isNotZero();
assertThat(result.getUpdatedAt()).isNotZero();
@Test
public void insert_with_upsert() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.upsert(dbSession, new NewCodePeriodDto()
- .setUuid(NEW_CODE_PERIOD_UUID)
- .setProjectUuid("proj-uuid")
- .setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.NUMBER_OF_DAYS)
- .setValue("5"));
+ insert("proj-uuid", "branch-uuid", NUMBER_OF_DAYS, "5");
- Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, NEW_CODE_PERIOD_UUID);
+ Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, "1");
assertThat(resultOpt)
.isNotNull()
.isNotEmpty();
NewCodePeriodDto result = resultOpt.get();
- assertThat(result.getUuid()).isEqualTo(NEW_CODE_PERIOD_UUID);
+ assertThat(result.getUuid()).isEqualTo("1");
assertThat(result.getProjectUuid()).isEqualTo("proj-uuid");
assertThat(result.getBranchUuid()).isEqualTo("branch-uuid");
- assertThat(result.getType()).isEqualTo(NewCodePeriodType.NUMBER_OF_DAYS);
+ assertThat(result.getType()).isEqualTo(NUMBER_OF_DAYS);
assertThat(result.getValue()).isEqualTo("5");
assertThat(result.getCreatedAt()).isNotZero();
assertThat(result.getUpdatedAt()).isNotZero();
@Test
public void update_with_upsert() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.NUMBER_OF_DAYS)
- .setValue("5"));
+ insert("proj-uuid", "branch-uuid", NUMBER_OF_DAYS, "5");
underTest.upsert(dbSession, new NewCodePeriodDto()
- .setUuid(NEW_CODE_PERIOD_UUID)
+ .setUuid("1")
.setProjectUuid("proj-uuid")
.setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
+ .setType(SPECIFIC_ANALYSIS)
.setValue("analysis-uuid"));
- Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, NEW_CODE_PERIOD_UUID);
+ Optional<NewCodePeriodDto> resultOpt = underTest.selectByUuid(dbSession, "1");
assertThat(resultOpt)
.isNotNull()
.isNotEmpty();
NewCodePeriodDto result = resultOpt.get();
- assertThat(result.getUuid()).isEqualTo(NEW_CODE_PERIOD_UUID);
+ assertThat(result.getUuid()).isEqualTo("1");
assertThat(result.getProjectUuid()).isEqualTo("proj-uuid");
assertThat(result.getBranchUuid()).isEqualTo("branch-uuid");
- assertThat(result.getType()).isEqualTo(NewCodePeriodType.SPECIFIC_ANALYSIS);
+ assertThat(result.getType()).isEqualTo(SPECIFIC_ANALYSIS);
assertThat(result.getValue()).isEqualTo("analysis-uuid");
assertThat(result.getCreatedAt()).isNotZero();
assertThat(result.getUpdatedAt()).isNotZero();
@Test
public void select_by_project_and_branch_uuids() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.NUMBER_OF_DAYS)
- .setValue("5"));
+ insert("proj-uuid", "branch-uuid", NUMBER_OF_DAYS, "5");
Optional<NewCodePeriodDto> resultOpt = underTest.selectByBranch(dbSession, "proj-uuid", "branch-uuid");
assertThat(resultOpt)
.isNotEmpty();
NewCodePeriodDto result = resultOpt.get();
- assertThat(result.getUuid()).isEqualTo(NEW_CODE_PERIOD_UUID);
+ assertThat(result.getUuid()).isEqualTo("1");
assertThat(result.getProjectUuid()).isEqualTo("proj-uuid");
assertThat(result.getBranchUuid()).isEqualTo("branch-uuid");
- assertThat(result.getType()).isEqualTo(NewCodePeriodType.NUMBER_OF_DAYS);
+ assertThat(result.getType()).isEqualTo(NUMBER_OF_DAYS);
assertThat(result.getValue()).isEqualTo("5");
assertThat(result.getCreatedAt()).isNotZero();
assertThat(result.getUpdatedAt()).isNotZero();
}
@Test
- public void select_by_project_uuid() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
+ public void select_branches_referencing() {
+ ProjectDto project = db.components().insertPrivateProjectDto();
+ BranchDto mainBranch = db.getDbClient().branchDao().selectByUuid(dbSession, project.getUuid()).get();
+ BranchDto branch1 = db.components().insertProjectBranch(project);
+ BranchDto branch2 = db.components().insertProjectBranch(project);
+ BranchDto branch3 = db.components().insertProjectBranch(project);
+
+ insert(project.getUuid(), null, REFERENCE_BRANCH, mainBranch.getKey());
+ insert(project.getUuid(), branch1.getUuid(), REFERENCE_BRANCH, mainBranch.getKey());
+ insert(project.getUuid(), branch2.getUuid(), NUMBER_OF_DAYS, "5");
+ insert(project.getUuid(), project.getUuid(), PREVIOUS_VERSION, null);
+ db.commit();
+ assertThat(underTest.selectBranchesReferencing(dbSession, project.getUuid(), mainBranch.getKey())).containsOnly(branch1.getUuid(), branch3.getUuid());
+ }
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setBranchUuid(null)
- .setType(NewCodePeriodType.NUMBER_OF_DAYS)
- .setValue("5"));
+ @Test
+ public void select_by_project_uuid() {
+ insert("proj-uuid", null, NUMBER_OF_DAYS, "5");
Optional<NewCodePeriodDto> resultOpt = underTest.selectByProject(dbSession, "proj-uuid");
assertThat(resultOpt)
.isNotEmpty();
NewCodePeriodDto result = resultOpt.get();
- assertThat(result.getUuid()).isEqualTo(NEW_CODE_PERIOD_UUID);
+ assertThat(result.getUuid()).isEqualTo("1");
assertThat(result.getProjectUuid()).isEqualTo("proj-uuid");
assertThat(result.getBranchUuid()).isNull();
- assertThat(result.getType()).isEqualTo(NewCodePeriodType.NUMBER_OF_DAYS);
+ assertThat(result.getType()).isEqualTo(NUMBER_OF_DAYS);
assertThat(result.getValue()).isEqualTo("5");
assertThat(result.getCreatedAt()).isNotZero();
assertThat(result.getUpdatedAt()).isNotZero();
@Test
public void select_global() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid(null)
- .setBranchUuid(null)
- .setType(NewCodePeriodType.NUMBER_OF_DAYS)
- .setValue("30"));
+ insert(null, null, NUMBER_OF_DAYS, "30");
Optional<NewCodePeriodDto> newCodePeriodDto = underTest.selectGlobal(dbSession);
assertThat(newCodePeriodDto).isNotEmpty();
NewCodePeriodDto result = newCodePeriodDto.get();
- assertThat(result.getUuid()).isEqualTo(NEW_CODE_PERIOD_UUID);
+ assertThat(result.getUuid()).isEqualTo("1");
assertThat(result.getProjectUuid()).isNull();
assertThat(result.getBranchUuid()).isNull();
- assertThat(result.getType()).isEqualTo(NewCodePeriodType.NUMBER_OF_DAYS);
+ assertThat(result.getType()).isEqualTo(NUMBER_OF_DAYS);
assertThat(result.getValue()).isEqualTo("30");
assertThat(result.getCreatedAt()).isNotZero();
assertThat(result.getUpdatedAt()).isNotZero();
@Test
public void exists_by_project_analysis_is_true() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
- .setValue("analysis-uuid"));
+ insert("proj-uuid", "branch-uuid", SPECIFIC_ANALYSIS, "analysis-uuid");
boolean exists = underTest.existsByProjectAnalysisUuid(dbSession, "analysis-uuid");
assertThat(exists).isTrue();
@Test
public void delete_by_project_uuid_and_branch_uuid() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setBranchUuid("branch-uuid")
- .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
- .setValue("analysis-uuid"));
+ insert("proj-uuid", "branch-uuid", SPECIFIC_ANALYSIS, "analysis-uuid");
underTest.delete(dbSession, "proj-uuid", "branch-uuid");
db.commit();
@Test
public void delete_by_project_uuid() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setProjectUuid("proj-uuid")
- .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
- .setValue("analysis-uuid"));
+ insert("proj-uuid", null, SPECIFIC_ANALYSIS, "analysis-uuid");
underTest.delete(dbSession, "proj-uuid", null);
db.commit();
@Test
public void delete_global() {
- when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
-
- underTest.insert(dbSession, new NewCodePeriodDto()
- .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
- .setValue("analysis-uuid"));
+ insert(null, null, SPECIFIC_ANALYSIS, "analysis-uuid");
underTest.delete(dbSession, null, null);
db.commit();
assertThat(db.countRowsOfTable("new_code_periods"))
.isEqualTo(expected);
}
+
+ private void insert(@Nullable String projectUuid, @Nullable String branchUuid, NewCodePeriodType type, @Nullable String value) {
+ underTest.insert(dbSession, new NewCodePeriodDto()
+ .setProjectUuid(projectUuid)
+ .setBranchUuid(branchUuid)
+ .setType(type)
+ .setValue(value));
+ }
}
import org.sonar.server.platform.db.migration.version.v86.DbVersion86;
import org.sonar.server.platform.db.migration.version.v87.DbVersion87;
import org.sonar.server.platform.db.migration.version.v88.DbVersion88;
+import org.sonar.server.platform.db.migration.version.v89.DbVersion89;
public class MigrationConfigurationModule extends Module {
@Override
DbVersion86.class,
DbVersion87.class,
DbVersion88.class,
+ DbVersion89.class,
// migration steps
MigrationStepRegistryImpl.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.v89;
+
+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.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddIndicesToNewCodePeriodTable extends DdlChange {
+ private static final String TABLE_NAME = "new_code_periods";
+ private static final String TYPE_INDEX_NAME = "idx_ncp_type";
+ private static final String VALUE_INDEX_NAME = "idx_ncp_value";
+
+ public AddIndicesToNewCodePeriodTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ if (!indexExists(TYPE_INDEX_NAME)) {
+ context.execute(new CreateIndexBuilder()
+ .setUnique(false)
+ .setTable(TABLE_NAME)
+ .setName(TYPE_INDEX_NAME)
+ .addColumn(newVarcharColumnDefBuilder()
+ .setColumnName("type")
+ .setIsNullable(false)
+ .setLimit(30)
+ .build())
+ .build());
+ }
+
+ if (!indexExists(VALUE_INDEX_NAME)) {
+ context.execute(new CreateIndexBuilder()
+ .setUnique(false)
+ .setTable(TABLE_NAME)
+ .setName(VALUE_INDEX_NAME)
+ .addColumn(newVarcharColumnDefBuilder()
+ .setColumnName("value")
+ .setIsNullable(true)
+ .setLimit(VarcharColumnDef.UUID_SIZE)
+ .build())
+ .build());
+ }
+ }
+
+ private boolean indexExists(String index) throws SQLException {
+ try (Connection connection = getDatabase().getDataSource().getConnection()) {
+ return DatabaseUtils.indexExistsIgnoreCase(TABLE_NAME, index, connection);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.v89;
+
+import org.sonar.server.platform.db.migration.step.MigrationStepRegistry;
+import org.sonar.server.platform.db.migration.version.DbVersion;
+
+public class DbVersion89 implements DbVersion {
+
+ @Override
+ public void addSteps(MigrationStepRegistry registry) {
+ registry
+ .add(4400, "Add indices on columns 'type' and 'value' to 'new_code_periods' table", AddIndicesToNewCodePeriodTable.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.v89;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+public class AddIndicesToNewCodePeriodTableTest {
+ private static final String TABLE_NAME = "new_code_periods";
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(AddIndicesToNewCodePeriodTableTest.class, "schema.sql");
+
+ private final MigrationStep underTest = new AddIndicesToNewCodePeriodTable(db.database());
+
+ @Test
+ public void execute() throws SQLException {
+ underTest.execute();
+
+ db.assertIndex(TABLE_NAME, "idx_ncp_type", "type");
+ db.assertIndex(TABLE_NAME, "idx_ncp_value", "value");
+ }
+
+ @Test
+ public void migration_is_re_entrant() throws SQLException {
+ underTest.execute();
+
+ // re-entrant
+ underTest.execute();
+
+ db.assertIndex(TABLE_NAME, "idx_ncp_type", "type");
+ db.assertIndex(TABLE_NAME, "idx_ncp_value", "value");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.v89;
+
+import org.junit.Test;
+import org.sonar.server.platform.db.migration.version.DbVersion;
+
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationNotEmpty;
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber;
+
+public class DbVersion89Test {
+
+ private final DbVersion underTest = new DbVersion89();
+
+ @Test
+ public void migrationNumber_starts_at_4400() {
+ verifyMinimumMigrationNumber(underTest, 4400);
+ }
+
+ @Test
+ public void verify_migration_count() {
+ verifyMigrationNotEmpty(underTest);
+ }
+
+}
--- /dev/null
+CREATE TABLE "NEW_CODE_PERIODS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40),
+ "BRANCH_UUID" VARCHAR(40),
+ "TYPE" VARCHAR(30) NOT NULL,
+ "VALUE" VARCHAR(40),
+ "UPDATED_AT" BIGINT NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "NEW_CODE_PERIODS" ADD CONSTRAINT "PK_NEW_CODE_PERIODS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_NEW_CODE_PERIODS" ON "NEW_CODE_PERIODS"("PROJECT_UUID", "BRANCH_UUID");