瀏覽代碼

SONAR-14646 copy issue state on branch merge

tags/8.9.0.43852
Michal Duda 3 年之前
父節點
當前提交
a4c77c9e98
共有 25 個文件被更改,包括 472 次插入165 次删除
  1. 24
    23
      server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabHttpClientTest.java
  2. 31
    10
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssues.java
  3. 4
    4
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java
  4. 9
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java
  5. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMerger.java
  6. 1
    4
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssuesLoader.java
  7. 3
    1
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java
  8. 1
    1
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java
  9. 8
    11
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMergerTest.java
  10. 13
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
  11. 4
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
  12. 5
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodDao.java
  13. 2
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodDto.java
  14. 3
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodMapper.java
  15. 14
    1
      server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
  16. 25
    0
      server/sonar-db-dao/src/main/resources/org/sonar/db/newcodeperiod/NewCodePeriodMapper.xml
  17. 2
    0
      server/sonar-db-dao/src/schema/schema-sq.ddl
  18. 39
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
  19. 67
    102
      server/sonar-db-dao/src/test/java/org/sonar/db/newcodeperiod/NewCodePeriodDaoTest.java
  20. 2
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java
  21. 75
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v89/AddIndicesToNewCodePeriodTable.java
  22. 32
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v89/DbVersion89.java
  23. 54
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v89/AddIndicesToNewCodePeriodTableTest.java
  24. 42
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v89/DbVersion89Test.java
  25. 11
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v89/AddIndicesToNewCodePeriodTableTest/schema.sql

+ 24
- 23
server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabHttpClientTest.java 查看文件

@@ -39,7 +39,6 @@ import static org.assertj.core.api.Assertions.tuple;

public class GitlabHttpClientTest {


@Rule
public LogTester logTester = new LogTester();

@@ -132,7 +131,7 @@ public class GitlabHttpClientTest {
}

@Test
public void get_branches(){
public void get_branches() {
MockResponse response = new MockResponse()
.setResponseCode(200)
.setBody("[{\n"
@@ -219,12 +218,12 @@ public class GitlabHttpClientTest {
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();
@@ -320,8 +319,8 @@ public class GitlabHttpClientTest {
.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
@@ -332,8 +331,8 @@ public class GitlabHttpClientTest {
.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
@@ -344,8 +343,8 @@ public class GitlabHttpClientTest {
.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
@@ -354,10 +353,10 @@ public class GitlabHttpClientTest {

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
@@ -366,10 +365,10 @@ public class GitlabHttpClientTest {

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
@@ -378,9 +377,11 @@ public class GitlabHttpClientTest {

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());
}
}

+ 31
- 10
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssues.java 查看文件

@@ -33,7 +33,9 @@ import org.sonar.db.component.KeyWithUuidDto;
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;
@@ -52,18 +54,37 @@ public class SiblingComponentsWithOpenIssues {
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());
}
}


+ 4
- 4
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java 查看文件

@@ -31,6 +31,7 @@ import org.sonar.core.issue.DefaultIssueComment;
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;

@@ -58,8 +59,7 @@ public class IssueLifecycle {
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;
@@ -102,9 +102,9 @@ public class IssueLifecycle {
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);
}

+ 9
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java 查看文件

@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
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 {
@@ -35,9 +36,11 @@ 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;
@@ -45,6 +48,7 @@ public class SiblingIssue implements Trackable {
this.ruleKey = ruleKey;
this.status = status;
this.prKey = prKey;
this.branchType = branchType;
this.updateDate = updateDate;
}

@@ -64,6 +68,10 @@ public class SiblingIssue implements Trackable {
return message;
}

public BranchType getBranchType() {
return branchType;
}

@CheckForNull
@Override
public String getLineHash() {

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMerger.java 查看文件

@@ -55,7 +55,7 @@ public class SiblingsIssueMerger {

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());
}
}
}

+ 1
- 4
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssuesLoader.java 查看文件

@@ -25,13 +25,11 @@ import java.util.List;
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;
@@ -69,9 +67,8 @@ public class SiblingsIssuesLoader {
}

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) {

+ 3
- 1
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java 查看文件

@@ -19,6 +19,7 @@
*/
package org.sonar.ce.task.projectanalysis.component;

import java.util.Collections;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
@@ -29,8 +30,8 @@ import org.sonar.db.DbTester;
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;
@@ -64,6 +65,7 @@ public class SiblingComponentsWithOpenIssuesTest {
@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,

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java 查看文件

@@ -145,7 +145,7 @@ public class IssueLifecycleTest {
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");

+ 8
- 11
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMergerTest.java 查看文件

@@ -27,8 +27,6 @@ import org.junit.Before;
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;
@@ -48,9 +46,11 @@ import org.sonar.db.issue.IssueDto;
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;
@@ -58,11 +58,8 @@ import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builde
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();
@@ -102,7 +99,6 @@ public class SiblingsIssueMergerTest {

@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),
@@ -124,6 +120,7 @@ public class SiblingsIssueMergerTest {
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
@@ -150,7 +147,7 @@ public class SiblingsIssueMergerTest {
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");
}
@@ -169,7 +166,7 @@ public class SiblingsIssueMergerTest {
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");
}
@@ -186,7 +183,7 @@ public class SiblingsIssueMergerTest {
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();

+ 13
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java 查看文件

@@ -302,8 +302,19 @@ public class ComponentDao implements Dao {
* 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));
}

/**

+ 4
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java 查看文件

@@ -138,8 +138,10 @@ public interface ComponentMapper {

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();


+ 5
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodDao.java 查看文件

@@ -21,6 +21,7 @@ package org.sonar.db.newcodeperiod;

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;
@@ -89,6 +90,10 @@ public class NewCodePeriodDao implements Dao {
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;

+ 2
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodDto.java 查看文件

@@ -91,11 +91,12 @@ public class NewCodePeriodDto {
return this;
}

@CheckForNull
public String getValue() {
return value;
}

public NewCodePeriodDto setValue(String value) {
public NewCodePeriodDto setValue(@Nullable String value) {
this.value = value;
return this;
}

+ 3
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodMapper.java 查看文件

@@ -21,6 +21,7 @@ package org.sonar.db.newcodeperiod;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.ibatis.annotations.Param;

public interface NewCodePeriodMapper {
@@ -39,6 +40,8 @@ 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);

+ 14
- 1
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml 查看文件

@@ -678,7 +678,7 @@
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
@@ -690,6 +690,19 @@
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

+ 25
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/newcodeperiod/NewCodePeriodMapper.xml 查看文件

@@ -70,6 +70,31 @@
</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"/>

+ 2
- 0
server/sonar-db-dao/src/schema/schema-sq.ddl 查看文件

@@ -490,6 +490,8 @@ CREATE TABLE "NEW_CODE_PERIODS"(
);
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,

+ 39
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java 查看文件

@@ -20,6 +20,7 @@
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;
@@ -49,7 +50,10 @@ import org.sonar.api.utils.System2;
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;
@@ -65,6 +69,9 @@ import static org.assertj.core.api.Assertions.assertThat;
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;
@@ -1799,6 +1806,38 @@ public class ComponentDaoTest {
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();
}
@@ -1818,5 +1857,4 @@ public class ComponentDaoTest {
return t -> {
};
}

}

+ 67
- 102
server/sonar-db-dao/src/test/java/org/sonar/db/newcodeperiod/NewCodePeriodDaoTest.java 查看文件

@@ -20,17 +20,23 @@
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 {

@@ -38,30 +44,23 @@ 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();
@@ -77,31 +76,25 @@ public class NewCodePeriodDaoTest {

@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();
@@ -112,26 +105,19 @@ public class NewCodePeriodDaoTest {

@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();
@@ -142,32 +128,26 @@ public class NewCodePeriodDaoTest {

@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();
@@ -178,13 +158,7 @@ public class NewCodePeriodDaoTest {

@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)
@@ -192,24 +166,34 @@ public class NewCodePeriodDaoTest {
.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)
@@ -217,10 +201,10 @@ public class NewCodePeriodDaoTest {
.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();
@@ -228,22 +212,16 @@ public class NewCodePeriodDaoTest {

@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();
@@ -251,13 +229,7 @@ public class NewCodePeriodDaoTest {

@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();
@@ -265,13 +237,7 @@ public class NewCodePeriodDaoTest {

@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();
@@ -280,12 +246,7 @@ public class NewCodePeriodDaoTest {

@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();
@@ -294,11 +255,7 @@ public class NewCodePeriodDaoTest {

@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();
@@ -336,4 +293,12 @@ public class NewCodePeriodDaoTest {
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));
}
}

+ 2
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java 查看文件

@@ -36,6 +36,7 @@ import org.sonar.server.platform.db.migration.version.v85.DbVersion85;
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
@@ -52,6 +53,7 @@ public class MigrationConfigurationModule extends Module {
DbVersion86.class,
DbVersion87.class,
DbVersion88.class,
DbVersion89.class,

// migration steps
MigrationStepRegistryImpl.class,

+ 75
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v89/AddIndicesToNewCodePeriodTable.java 查看文件

@@ -0,0 +1,75 @@
/*
* 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);
}
}
}

+ 32
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v89/DbVersion89.java 查看文件

@@ -0,0 +1,32 @@
/*
* 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);
}
}

+ 54
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v89/AddIndicesToNewCodePeriodTableTest.java 查看文件

@@ -0,0 +1,54 @@
/*
* 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");
}
}

+ 42
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v89/DbVersion89Test.java 查看文件

@@ -0,0 +1,42 @@
/*
* 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);
}

}

+ 11
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v89/AddIndicesToNewCodePeriodTableTest/schema.sql 查看文件

@@ -0,0 +1,11 @@
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");

Loading…
取消
儲存