aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java47
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java57
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasures.java5
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRoot.java75
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java11
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java7
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java50
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java87
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRootTest.java95
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java40
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ValidateProjectStepTest.java22
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java10
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentWithModuleUuidDto.java47
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml9
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java28
18 files changed, 515 insertions, 81 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java
index 68080a5fa07..91479aee677 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java
@@ -31,6 +31,7 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.internal.apachecommons.lang.StringUtils;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
+import org.sonar.ce.task.projectanalysis.issue.IssueRelocationToRoot;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.component.SnapshotDto;
import org.sonar.scanner.protocol.output.ScannerReport;
@@ -67,6 +68,7 @@ public class ComponentTreeBuilder {
private final Branch branch;
@Nullable
private final SnapshotDto baseAnalysis;
+ private final IssueRelocationToRoot issueRelocationToRoot;
private ScannerReport.Component rootComponent;
private String scmBasePath;
@@ -77,7 +79,7 @@ public class ComponentTreeBuilder {
Function<String, String> uuidSupplier,
Function<Integer, ScannerReport.Component> scannerComponentSupplier,
Project project,
- Branch branch, @Nullable SnapshotDto baseAnalysis) {
+ Branch branch, @Nullable SnapshotDto baseAnalysis, IssueRelocationToRoot issueRelocationToRoot) {
this.keyGenerator = keyGenerator;
this.publicKeyGenerator = publicKeyGenerator;
@@ -86,17 +88,18 @@ public class ComponentTreeBuilder {
this.project = project;
this.branch = branch;
this.baseAnalysis = baseAnalysis;
+ this.issueRelocationToRoot = issueRelocationToRoot;
}
public Component buildProject(ScannerReport.Component project, String scmBasePath) {
this.rootComponent = project;
this.scmBasePath = trimToNull(scmBasePath);
- Node root = buildFolderHierarchy(project);
- return buildNode(root, "");
+ Node root = createProjectHierarchy(project);
+ return buildComponent(root, "");
}
- private Node buildFolderHierarchy(ScannerReport.Component rootComponent) {
+ private Node createProjectHierarchy(ScannerReport.Component rootComponent) {
Preconditions.checkArgument(rootComponent.getType() == ScannerReport.Component.ComponentType.PROJECT, "Expected root component of type 'PROJECT'");
LinkedList<ScannerReport.Component> queue = new LinkedList<>();
@@ -112,16 +115,11 @@ public class ComponentTreeBuilder {
ScannerReport.Component component = queue.removeFirst();
switch (component.getType()) {
case FILE:
- addFileOrDirectory(root, component);
+ addFile(root, component);
break;
case MODULE:
-
- component.getChildRefList().stream()
- .map(scannerComponentSupplier)
- .forEach(queue::addLast);
- break;
case DIRECTORY:
- addFileOrDirectory(root, component);
+ issueRelocationToRoot.relocate(rootComponent, component);
component.getChildRefList().stream()
.map(scannerComponentSupplier)
.forEach(queue::addLast);
@@ -133,9 +131,8 @@ public class ComponentTreeBuilder {
return root;
}
- private static void addFileOrDirectory(Node root, ScannerReport.Component file) {
- Preconditions.checkArgument(file.getType() != ScannerReport.Component.ComponentType.FILE || !StringUtils.isEmpty(file.getProjectRelativePath()),
- "Files should have a relative path: " + file);
+ private static void addFile(Node root, ScannerReport.Component file) {
+ Preconditions.checkArgument(!StringUtils.isEmpty(file.getProjectRelativePath()), "Files should have a project relative path: " + file);
String[] split = StringUtils.split(file.getProjectRelativePath(), '/');
Node currentNode = root.children().computeIfAbsent("", k -> new Node());
@@ -145,7 +142,7 @@ public class ComponentTreeBuilder {
currentNode.reportComponent = file;
}
- private Component buildNode(Node node, String currentPath) {
+ private Component buildComponent(Node node, String currentPath) {
List<Component> childComponents = buildChildren(node, currentPath);
ScannerReport.Component component = node.reportComponent();
@@ -157,7 +154,7 @@ public class ComponentTreeBuilder {
}
}
- return buildDirectory(currentPath, component, childComponents);
+ return buildDirectory(currentPath, childComponents);
}
private List<Component> buildChildren(Node node, String currentPath) {
@@ -165,14 +162,15 @@ public class ComponentTreeBuilder {
for (Map.Entry<String, Node> e : node.children().entrySet()) {
String path = buildPath(currentPath, e.getKey());
- Node n = e.getValue();
+ Node childNode = e.getValue();
- while (n.children().size() == 1 && n.children().values().iterator().next().children().size() > 0) {
- Map.Entry<String, Node> childEntry = n.children().entrySet().iterator().next();
+ // collapse folders that only contain one folder
+ while (childNode.children().size() == 1 && childNode.children().values().iterator().next().children().size() > 0) {
+ Map.Entry<String, Node> childEntry = childNode.children().entrySet().iterator().next();
path = buildPath(path, childEntry.getKey());
- n = childEntry.getValue();
+ childNode = childEntry.getValue();
}
- children.add(buildNode(n, path));
+ children.add(buildComponent(childNode, path));
}
return children;
}
@@ -216,18 +214,17 @@ public class ComponentTreeBuilder {
.build();
}
- private ComponentImpl buildDirectory(String path, @Nullable ScannerReport.Component scannerComponent, List<Component> children) {
+ private ComponentImpl buildDirectory(String path, List<Component> children) {
String nonEmptyPath = path.isEmpty() ? "/" : path;
String key = keyGenerator.generateKey(rootComponent, nonEmptyPath);
String publicKey = publicKeyGenerator.generateKey(rootComponent, nonEmptyPath);
- Integer ref = scannerComponent != null ? scannerComponent.getRef() : null;
return ComponentImpl.builder(Component.Type.DIRECTORY)
.setUuid(uuidSupplier.apply(key))
.setDbKey(key)
.setKey(publicKey)
.setName(publicKey)
.setStatus(convertStatus(FileStatus.UNAVAILABLE))
- .setReportAttributes(createAttributesBuilder(ref, nonEmptyPath, scmBasePath, path).build())
+ .setReportAttributes(createAttributesBuilder(null, nonEmptyPath, scmBasePath, path).build())
.addChildren(children)
.build();
}
@@ -359,7 +356,7 @@ public class ComponentTreeBuilder {
@CheckForNull
private static String computeScmPath(@Nullable String scmBasePath, String scmRelativePath) {
if (scmRelativePath.isEmpty()) {
- return null;
+ return scmBasePath;
}
if (scmBasePath == null) {
return scmRelativePath;
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java
index 58e5ac7041e..b87e740492a 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java
@@ -22,20 +22,69 @@ package org.sonar.ce.task.projectanalysis.component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.core.component.ComponentKeys;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentWithModuleUuidDto;
import org.sonar.db.component.KeyWithUuidDto;
public class ComponentUuidFactory {
-
private final Map<String, String> uuidsByKey = new HashMap<>();
public ComponentUuidFactory(DbClient dbClient, DbSession dbSession, String rootKey) {
- List<KeyWithUuidDto> keys = dbClient.componentDao().selectUuidsByKeyFromProjectKey(dbSession, rootKey);
- for (KeyWithUuidDto dto : keys) {
- uuidsByKey.put(dto.key(), dto.uuid());
+ Map<String, String> modulePathsByUuid = loadModulePathsByUuid(dbClient, dbSession, rootKey);
+
+ if (modulePathsByUuid.isEmpty()) {
+ // only contains root project
+ List<KeyWithUuidDto> keys = dbClient.componentDao().selectUuidsByKeyFromProjectKey(dbSession, rootKey);
+ keys.forEach(dto -> uuidsByKey.put(dto.key(), dto.uuid()));
+ } else {
+ List<ComponentWithModuleUuidDto> dtos = loadComponentsWithModuleUuid(dbClient, dbSession, rootKey);
+ for (ComponentWithModuleUuidDto dto : dtos) {
+ String pathFromRootProject = modulePathsByUuid.get(dto.moduleUuid());
+ String componentPath = StringUtils.isEmpty(pathFromRootProject) ? dto.path() : (pathFromRootProject + "/" + dto.path());
+ uuidsByKey.put(ComponentKeys.createEffectiveKey(rootKey, componentPath), dto.uuid());
+ }
+ }
+ }
+
+ private static List<ComponentWithModuleUuidDto> loadComponentsWithModuleUuid(DbClient dbClient, DbSession dbSession, String rootKey) {
+ return dbClient.componentDao().selectComponentsWithModuleUuidFromProjectKey(dbSession, rootKey);
+ }
+
+ private static Map<String, String> loadModulePathsByUuid(DbClient dbClient, DbSession dbSession, String rootKey) {
+ List<ComponentDto> moduleDtos = dbClient.componentDao()
+ .selectEnabledModulesFromProjectKey(dbSession, rootKey, false).stream()
+ .filter(c -> Qualifiers.MODULE.equals(c.qualifier()))
+ .collect(Collectors.toList());
+
+ Map<String, ComponentDto> dtoByUuid = moduleDtos.stream()
+ .collect(Collectors.toMap(ComponentDto::uuid, dto -> dto));
+
+ Map<String, String> modulePathByUuid = new HashMap<>();
+
+ for (ComponentDto dto : moduleDtos) {
+ String modulePath = null;
+ ComponentDto currentDto = dto;
+ while (currentDto != null && currentDto.moduleUuid() != null) {
+ String path = currentDto.path();
+ if (modulePath == null) {
+ modulePath = path;
+ } else {
+ modulePath = path + "/" + modulePath;
+ }
+ currentDto = dtoByUuid.get(currentDto.moduleUuid());
+ }
+
+ modulePathByUuid.put(dto.uuid(), modulePath);
}
+
+ return modulePathByUuid;
}
/**
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
index 051b90d4c27..7f661f1cde3 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
@@ -64,6 +64,7 @@ import org.sonar.ce.task.projectanalysis.issue.IssueCache;
import org.sonar.ce.task.projectanalysis.issue.IssueCounter;
import org.sonar.ce.task.projectanalysis.issue.IssueCreationDateCalculator;
import org.sonar.ce.task.projectanalysis.issue.IssueLifecycle;
+import org.sonar.ce.task.projectanalysis.issue.IssueRelocationToRoot;
import org.sonar.ce.task.projectanalysis.issue.IssueTrackingDelegator;
import org.sonar.ce.task.projectanalysis.issue.IssueVisitors;
import org.sonar.ce.task.projectanalysis.issue.IssuesRepositoryVisitor;
@@ -232,6 +233,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
ComponentsWithUnprocessedIssues.class,
ComponentIssuesRepositoryImpl.class,
IssueFilter.class,
+ IssueRelocationToRoot.class,
// common rules
CommonRuleEngineImpl.class,
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasures.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasures.java
index 861c1afe725..f29477995f3 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasures.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasures.java
@@ -156,10 +156,7 @@ public class DuplicationMeasures {
private static int getMeasure(CounterInitializationContext context, String metricKey) {
Optional<Measure> files = context.getMeasure(metricKey);
- if (files.isPresent()) {
- return files.get().getIntValue();
- }
- return 0;
+ return files.map(Measure::getIntValue).orElse(0);
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRoot.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRoot.java
new file mode 100644
index 00000000000..af6f87a75e1
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRoot.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+public class IssueRelocationToRoot {
+ private final BatchReportReader reader;
+ private final List<ScannerReport.Issue> movedIssues = new ArrayList<>();
+
+ public IssueRelocationToRoot(BatchReportReader reader) {
+ this.reader = reader;
+ }
+
+ public List<ScannerReport.Issue> getMovedIssues() {
+ return Collections.unmodifiableList(movedIssues);
+ }
+
+ public void relocate(ScannerReport.Component root, ScannerReport.Component component) {
+ CloseableIterator<ScannerReport.Issue> issueIt = reader.readComponentIssues(component.getRef());
+ while (issueIt.hasNext()) {
+ ScannerReport.Issue issue = issueIt.next();
+ movedIssues.add(moveIssueToRoot(root, component, issue));
+ }
+ }
+
+ private static ScannerReport.Issue moveIssueToRoot(ScannerReport.Component root, ScannerReport.Component component, ScannerReport.Issue issue) {
+ return ScannerReport.Issue.newBuilder(issue)
+ .clearFlow()
+ .clearTextRange()
+ .addAllFlow(issue.getFlowList().stream()
+ .map(flow -> moveFlow(root, component, flow))
+ .collect(Collectors.toList()))
+ .build();
+ }
+
+ private static ScannerReport.IssueLocation moveLocation(ScannerReport.Component root, ScannerReport.Component component, ScannerReport.IssueLocation location) {
+ String msg = "[" + component.getProjectRelativePath() + "] " + location.getMsg();
+ return ScannerReport.IssueLocation.newBuilder()
+ .setComponentRef(root.getRef())
+ .setMsg(msg)
+ .build();
+ }
+
+ private static ScannerReport.Flow moveFlow(ScannerReport.Component root, ScannerReport.Component component, ScannerReport.Flow flow) {
+ return ScannerReport.Flow.newBuilder()
+ .addAllLocation(flow.getLocationList().stream()
+ .map(location -> moveLocation(root, component, location))
+ .collect(Collectors.toList()))
+ .build();
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
index 2cd65c16a0a..eb44e5e43d8 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
@@ -61,10 +61,11 @@ public class TrackerRawInputFactory {
private final SourceLinesHashRepository sourceLinesHash;
private final RuleRepository ruleRepository;
private final ActiveRulesHolder activeRulesHolder;
+ private final IssueRelocationToRoot issueRelocationToRoot;
public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader,
SourceLinesHashRepository sourceLinesHash, CommonRuleEngine commonRuleEngine, IssueFilter issueFilter, RuleRepository ruleRepository,
- ActiveRulesHolder activeRulesHolder) {
+ ActiveRulesHolder activeRulesHolder, IssueRelocationToRoot issueRelocationToRoot) {
this.treeRootHolder = treeRootHolder;
this.reportReader = reportReader;
this.sourceLinesHash = sourceLinesHash;
@@ -72,6 +73,7 @@ public class TrackerRawInputFactory {
this.issueFilter = issueFilter;
this.ruleRepository = ruleRepository;
this.activeRulesHolder = activeRulesHolder;
+ this.issueRelocationToRoot = issueRelocationToRoot;
}
public Input<DefaultIssue> create(Component component) {
@@ -144,6 +146,13 @@ public class TrackerRawInputFactory {
}
}
+ if (component.getType() == Component.Type.PROJECT) {
+ // TODO apply filters?
+ issueRelocationToRoot.getMovedIssues().stream()
+ .map(i -> toIssue(getLineHashSequence(), i))
+ .forEach(result::add);
+ }
+
return result;
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java
index f4b73f412d3..342e5cdbc81 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java
@@ -31,6 +31,7 @@ import org.sonar.ce.task.projectanalysis.component.ComponentTreeBuilder;
import org.sonar.ce.task.projectanalysis.component.ComponentUuidFactory;
import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl;
import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolder;
+import org.sonar.ce.task.projectanalysis.issue.IssueRelocationToRoot;
import org.sonar.ce.task.step.ComputationStep;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -47,13 +48,15 @@ public class BuildComponentTreeStep implements ComputationStep {
private final BatchReportReader reportReader;
private final MutableTreeRootHolder treeRootHolder;
private final MutableAnalysisMetadataHolder analysisMetadataHolder;
+ private final IssueRelocationToRoot issueRelocationToRoot;
public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader,
- MutableTreeRootHolder treeRootHolder, MutableAnalysisMetadataHolder analysisMetadataHolder) {
+ MutableTreeRootHolder treeRootHolder, MutableAnalysisMetadataHolder analysisMetadataHolder, IssueRelocationToRoot issueRelocationToRoot) {
this.dbClient = dbClient;
this.reportReader = reportReader;
this.treeRootHolder = treeRootHolder;
this.analysisMetadataHolder = analysisMetadataHolder;
+ this.issueRelocationToRoot = issueRelocationToRoot;
}
@Override
@@ -82,7 +85,7 @@ public class BuildComponentTreeStep implements ComputationStep {
reportReader::readComponent,
analysisMetadataHolder.getProject(),
analysisMetadataHolder.getBranch(),
- baseAnalysis);
+ baseAnalysis, issueRelocationToRoot);
String relativePathFromScmRoot = reportReader.readMetadata().getRelativePathFromScmRoot();
Component reportTreeRoot = builder.buildProject(reportProject, relativePathFromScmRoot);
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java
index f5fe6f8ab54..9f4920dac21 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java
@@ -43,6 +43,8 @@ import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
@@ -64,6 +66,7 @@ public class ComponentTreeBuilderTest {
private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, MODULE, DIRECTORY, FILE);
private static final String NO_SCM_BASE_PATH = "";
+ private IssueRelocationToRoot issueRelocationToRoot = mock(IssueRelocationToRoot.class);
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -571,6 +574,39 @@ public class ComponentTreeBuilderTest {
}
@Test
+ public void issues_are_relocated_from_directories_and_modules_to_root() {
+ ScannerReport.Component project = newBuilder()
+ .setType(PROJECT)
+ .setKey("c1")
+ .setRef(1)
+ .addChildRef(2)
+ .build();
+ ScannerReport.Component.Builder module = newBuilder()
+ .setRef(2)
+ .setType(MODULE)
+ .setKey("c2")
+ .addChildRef(3);
+ scannerComponentProvider.add(module);
+ ScannerReport.Component.Builder directory = newBuilder()
+ .setRef(3)
+ .setType(DIRECTORY)
+ .setProjectRelativePath("src/js")
+ .addChildRef(4);
+ scannerComponentProvider.add(directory);
+ ScannerReport.Component.Builder file = newBuilder()
+ .setRef(4)
+ .setType(FILE)
+ .setProjectRelativePath("src/js/Foo.js")
+ .setLines(1);
+ scannerComponentProvider.add(file);
+
+ call(project);
+ verify(issueRelocationToRoot).relocate(project, module.build());
+ verify(issueRelocationToRoot).relocate(project, directory.build());
+ verifyNoMoreInteractions(issueRelocationToRoot);
+ }
+
+ @Test
public void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
ScannerReport.Component project = newBuilder()
.setType(PROJECT)
@@ -826,19 +862,7 @@ public class ComponentTreeBuilderTest {
Branch branch = mock(Branch.class);
when(branch.isMain()).thenReturn(mainBranch);
return new ComponentTreeBuilder(KEY_GENERATOR, PUBLIC_KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, branch, baseAnalysis,
- mock(IssueRelocationToRoot.class));
- }
-
- private static Map<Integer, Component> indexComponentByRef(Component root) {
- Map<Integer, Component> componentsByRef = new HashMap<>();
- new DepthTraversalTypeAwareCrawler(
- new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, PRE_ORDER) {
- @Override
- public void visitAny(Component any) {
- componentsByRef.put(any.getReportAttributes().getRef(), any);
- }
- }).visit(root);
- return componentsByRef;
+ issueRelocationToRoot);
}
private static Map<String, Component> indexComponentByKey(Component root) {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java
index 9b9f4c56196..90ad37a15cb 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java
@@ -36,11 +36,94 @@ public class ComponentUuidFactoryTest {
@Test
public void load_uuids_from_existing_components_in_db() {
ComponentDto project = db.components().insertPrivateProject();
- ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
+ ComponentDto module = db.components().insertComponent(ComponentTesting
+ .newModuleDto(project).setPath("module1"));
ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey());
+
+ assertThat(underTest.getOrCreateForKey(project.getDbKey())).isEqualTo(project.uuid());
+ assertThat(underTest.getOrCreateForKey(module.getDbKey())).isNotEqualTo(module.uuid());
+ }
+
+ @Test
+ public void migrate_project_with_modules() {
+ ComponentDto project = db.components().insertPrivateProject(dto -> dto.setDbKey("project"));
+ ComponentDto module1 = db.components().insertComponent(ComponentTesting.newModuleDto(project)
+ .setDbKey("project:module1")
+ .setPath("module1_path"));
+ ComponentDto module2 = db.components().insertComponent(ComponentTesting.newModuleDto(module1)
+ .setDbKey("project:module1:module2")
+ .setPath("module2_path"));
+ ComponentDto file1 = db.components().insertComponent(ComponentTesting.newFileDto(project)
+ .setDbKey("project:file1")
+ .setPath("file1_path"));
+ ComponentDto file2 = db.components().insertComponent(ComponentTesting.newFileDto(module2)
+ .setDbKey("project:module1:module2:file2")
+ .setPath("file2_path"));
+
+ assertThat(file2.moduleUuidPath()).isEqualTo("." + project.uuid() + "." + module1.uuid() + "." + module2.uuid() + ".");
+
+ ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey());
+
+ // migrated files
+ assertThat(underTest.getOrCreateForKey("project:file1_path")).isEqualTo(file1.uuid());
+ assertThat(underTest.getOrCreateForKey("project:module1_path/module2_path/file2_path")).isEqualTo(file2.uuid());
+
+ // project remains the same
assertThat(underTest.getOrCreateForKey(project.getDbKey())).isEqualTo(project.uuid());
- assertThat(underTest.getOrCreateForKey(module.getDbKey())).isEqualTo(module.uuid());
+
+ // old keys with modules don't exist
+ assertThat(underTest.getOrCreateForKey(module1.getDbKey())).isNotEqualTo(module1.uuid());
+ assertThat(underTest.getOrCreateForKey(module2.getDbKey())).isNotEqualTo(module2.uuid());
+ assertThat(underTest.getOrCreateForKey(file1.getDbKey())).isNotEqualTo(file1.uuid());
+ assertThat(underTest.getOrCreateForKey(file2.getDbKey())).isNotEqualTo(file2.uuid());
+ }
+
+ @Test
+ public void migrate_project_with_disabled_modules() {
+ ComponentDto project = db.components().insertPrivateProject(dto -> dto.setDbKey("project"));
+ ComponentDto module1 = db.components().insertComponent(ComponentTesting.newModuleDto(project)
+ .setDbKey("project:module1")
+ .setEnabled(false)
+ .setPath("module1_path"));
+ ComponentDto file1 = db.components().insertComponent(ComponentTesting.newFileDto(module1)
+ .setDbKey("project:file1")
+ .setEnabled(false)
+ .setPath("file1_path"));
+
+ ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey());
+
+ // migrated files
+ assertThat(underTest.getOrCreateForKey("project:module1_path/file1_path")).isEqualTo(file1.uuid());
+ }
+
+ @Test
+ public void migrate_project_having_modules_without_paths() {
+ ComponentDto project = db.components().insertPrivateProject(dto -> dto.setDbKey("project"));
+ ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project)
+ .setDbKey("project:module")
+ .setPath(null));
+ ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(module)
+ .setDbKey("project:module:file")
+ .setPath("file_path"));
+
+ assertThat(file.moduleUuidPath()).isEqualTo("." + project.uuid() + "." + module.uuid() + ".");
+
+ ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey());
+
+ // file will have this key since the module has a null path
+ assertThat(underTest.getOrCreateForKey("project:file_path")).isEqualTo(file.uuid());
+
+ // migrated module
+ // TODO!!
+ //assertThat(underTest.getOrCreateForKey("project:module")).isEqualTo(module.uuid());
+
+ // project remains the same
+ //assertThat(underTest.getOrCreateForKey(project.getDbKey())).isEqualTo(project.uuid());
+
+ // old keys with modules don't exist
+ assertThat(underTest.getOrCreateForKey(module.getDbKey())).isNotEqualTo(module.uuid());
+ assertThat(underTest.getOrCreateForKey(file.getDbKey())).isNotEqualTo(file.uuid());
}
@Test
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRootTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRootTest.java
new file mode 100644
index 00000000000..b344e5804f6
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueRelocationToRootTest.java
@@ -0,0 +1,95 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssueRelocationToRootTest {
+ @Rule
+ public BatchReportReaderRule reader = new BatchReportReaderRule();
+ private IssueRelocationToRoot underTest = new IssueRelocationToRoot(reader);
+
+ private ScannerReport.Component root;
+ private ScannerReport.Component module;
+ private ScannerReport.Component directory;
+
+ @Before
+ public void createComponents() {
+ root = ScannerReport.Component.newBuilder()
+ .setType(ScannerReport.Component.ComponentType.PROJECT)
+ .setRef(1)
+ .setStatus(ScannerReport.Component.FileStatus.UNAVAILABLE)
+ .setKey("PROJECT")
+ .addChildRef(2)
+ .build();
+ module = ScannerReport.Component.newBuilder()
+ .setType(ScannerReport.Component.ComponentType.MODULE)
+ .setRef(2)
+ .setStatus(ScannerReport.Component.FileStatus.UNAVAILABLE)
+ .setKey("PROJECT:MODULE")
+ .addChildRef(3)
+ .build();
+ directory = ScannerReport.Component.newBuilder()
+ .setType(ScannerReport.Component.ComponentType.DIRECTORY)
+ .setRef(3)
+ .setStatus(ScannerReport.Component.FileStatus.UNAVAILABLE)
+ .setKey("PROJECT:MODULE:DIRECTORY")
+ .addChildRef(4)
+ .build();
+
+ reader.putComponent(root);
+ reader.putComponent(module);
+ reader.putComponent(directory);
+ }
+
+ private void createIssues() {
+ reader.putIssues(2, Collections.singletonList(ScannerReport.Issue.newBuilder().setRuleKey("module_issue").build()));
+ reader.putIssues(3, Collections.singletonList(ScannerReport.Issue.newBuilder().setRuleKey("directory_issue").build()));
+ }
+
+ @Test
+ public void move_module_and_directory_issues() {
+ createComponents();
+ createIssues();
+
+ underTest.relocate(root, module);
+ underTest.relocate(root, directory);
+
+ assertThat(underTest.getMovedIssues()).hasSize(2);
+ assertThat(underTest.getMovedIssues()).extracting(ScannerReport.Issue::getRuleKey).containsOnly("module_issue", "directory_issue");
+ }
+
+ @Test
+ public void do_nothing_if_no_issues() {
+ createComponents();
+ underTest.relocate(root, module);
+ underTest.relocate(root, directory);
+
+ assertThat(underTest.getMovedIssues()).hasSize(0);
+ }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java
index 46afb379851..ba95929db6a 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java
@@ -168,22 +168,46 @@ public class BuildComponentTreeStepTest {
setAnalysisMetadataHolder();
OrganizationDto organizationDto = dbTester.organizations().insert();
ComponentDto project = insertComponent(newPrivateProjectDto(organizationDto, "ABCD").setDbKey(REPORT_PROJECT_KEY));
+ ComponentDto directory = newDirectory(project, "CDEF", REPORT_DIR_PATH_1);
+ insertComponent(directory.setDbKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1));
+ insertComponent(newFileDto(project, directory, "DEFG")
+ .setDbKey(REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1)
+ .setPath(REPORT_FILE_PATH_1));
+
+ // new structure, without modules
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, DIR_REF_1));
+ reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_PATH_1, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, "ABCD");
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, "CDEF");
+ verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, "DEFG");
+ }
+
+ @Test
+ public void return_existing_uuids_with_modules() {
+ setAnalysisMetadataHolder();
+ OrganizationDto organizationDto = dbTester.organizations().insert();
+ ComponentDto project = insertComponent(newPrivateProjectDto(organizationDto, "ABCD").setDbKey(REPORT_PROJECT_KEY));
ComponentDto module = insertComponent(newModuleDto("BCDE", project).setDbKey(REPORT_MODULE_KEY));
ComponentDto directory = newDirectory(module, "CDEF", REPORT_DIR_PATH_1);
insertComponent(directory.setDbKey(REPORT_MODULE_KEY + ":" + REPORT_DIR_PATH_1));
- insertComponent(newFileDto(module, directory, "DEFG").setDbKey(REPORT_MODULE_KEY + ":" + REPORT_FILE_PATH_1));
+ insertComponent(newFileDto(module, directory, "DEFG")
+ .setDbKey(REPORT_MODULE_KEY + ":" + REPORT_FILE_PATH_1)
+ .setPath(REPORT_FILE_PATH_1));
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
- reportReader.putComponent(component(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
- reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_PATH_1, FILE_1_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+ // new structure, without modules
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, DIR_REF_1));
+ reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, "module/" + REPORT_DIR_PATH_1, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, "module/" + REPORT_FILE_PATH_1));
underTest.execute(new TestComputationStepContext());
verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, "ABCD");
- // TODO migrate modules
- // verifyComponentByRef(DIR_REF_1, REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, "CDEF");
- // verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, "DEFG");
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":module/" + REPORT_DIR_PATH_1, REPORT_PROJECT_KEY + ":module/" + REPORT_DIR_PATH_1, "CDEF");
+ verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":module/" + REPORT_FILE_PATH_1, "DEFG");
}
@Test
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ValidateProjectStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ValidateProjectStepTest.java
index 6e472812af7..5f26eadf1e2 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ValidateProjectStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ValidateProjectStepTest.java
@@ -28,7 +28,6 @@ import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.System2;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl;
import org.sonar.ce.task.projectanalysis.component.ReportComponent;
@@ -39,8 +38,6 @@ import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.SnapshotTesting;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
public class ValidateProjectStepTest {
@@ -55,9 +52,6 @@ public class ValidateProjectStepTest {
public ExpectedException thrown = ExpectedException.none();
@Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule();
-
- @Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
@Rule
@@ -67,17 +61,10 @@ public class ValidateProjectStepTest {
DbClient dbClient = dbTester.getDbClient();
- ValidateProjectStep underTest = new ValidateProjectStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+ ValidateProjectStep underTest = new ValidateProjectStep(dbClient, treeRootHolder, analysisMetadataHolder);
@Test
public void not_fail_if_analysis_date_is_after_last_analysis() {
- reportReader.putComponent(ScannerReport.Component.newBuilder()
- .setRef(1)
- .setType(ComponentType.PROJECT)
- .setKey(PROJECT_KEY)
- .addChildRef(2)
- .build());
-
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), "ABCD").setDbKey(PROJECT_KEY);
dbClient.componentDao().insert(dbTester.getSession(), project);
dbClient.snapshotDao().insert(dbTester.getSession(), SnapshotTesting.newAnalysis(project).setCreatedAt(1420088400000L)); // 2015-01-01
@@ -92,13 +79,6 @@ public class ValidateProjectStepTest {
public void fail_if_analysis_date_is_before_last_analysis() {
analysisMetadataHolder.setAnalysisDate(DateUtils.parseDate("2015-01-01"));
- reportReader.putComponent(ScannerReport.Component.newBuilder()
- .setRef(1)
- .setType(ComponentType.PROJECT)
- .setKey(PROJECT_KEY)
- .addChildRef(2)
- .build());
-
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), "ABCD").setDbKey(PROJECT_KEY);
dbClient.componentDao().insert(dbTester.getSession(), project);
dbClient.snapshotDao().insert(dbTester.getSession(), SnapshotTesting.newAnalysis(project).setCreatedAt(1433131200000L)); // 2015-06-01
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
index 18c7509ffeb..5ce01ea804a 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
@@ -48,6 +48,7 @@ import org.sonar.db.ce.CeTaskMessageMapper;
import org.sonar.db.component.AnalysisPropertiesMapper;
import org.sonar.db.component.BranchMapper;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentWithModuleUuidDto;
import org.sonar.db.component.ComponentDtoWithSnapshotId;
import org.sonar.db.component.ComponentKeyUpdaterMapper;
import org.sonar.db.component.ComponentMapper;
@@ -167,6 +168,7 @@ public class MyBatis implements Startable {
confBuilder.loadAlias("ActiveRuleParam", ActiveRuleParamDto.class);
confBuilder.loadAlias("CeTaskCharacteristic", CeTaskCharacteristicDto.class);
confBuilder.loadAlias("Component", ComponentDto.class);
+ confBuilder.loadAlias("ComponentWithModuleUuid", ComponentWithModuleUuidDto.class);
confBuilder.loadAlias("ComponentWithSnapshot", ComponentDtoWithSnapshotId.class);
confBuilder.loadAlias("CustomMeasure", CustomMeasureDto.class);
confBuilder.loadAlias("DuplicationUnit", DuplicationUnitDto.class);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
index f6f79f3de77..c0449b593e6 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
@@ -183,8 +183,12 @@ public class ComponentDao implements Dao {
return mapper(session).selectUuidsByKeyFromProjectKey(projectKey);
}
+ public List<ComponentDto> selectEnabledModulesFromProjectKey(DbSession session, String projectKey, boolean excludeDisabled) {
+ return mapper(session).selectComponentsFromProjectKeyAndScope(projectKey, Scopes.PROJECT, excludeDisabled);
+ }
+
public List<ComponentDto> selectEnabledModulesFromProjectKey(DbSession session, String projectKey) {
- return mapper(session).selectComponentsFromProjectKeyAndScope(projectKey, Scopes.PROJECT, true);
+ return selectEnabledModulesFromProjectKey(session, projectKey, true);
}
public List<ComponentDto> selectByKeys(DbSession session, Collection<String> keys) {
@@ -347,6 +351,10 @@ public class ComponentDao implements Dao {
return new HashSet<>(mapper(dbSession).selectComponentsByQualifiers(qualifiers));
}
+ public List<ComponentWithModuleUuidDto> selectComponentsWithModuleUuidFromProjectKey(DbSession dbSession, String projectKey) {
+ return mapper(dbSession).selectComponentsWithModuleUuidFromProjectKey(projectKey);
+ }
+
public List<ComponentDto> selectProjectsByNameQuery(DbSession dbSession, @Nullable String nameQuery, boolean includeModules) {
String nameQueryForSql = nameQuery == null ? null : buildLikeValue(nameQuery, BEFORE_AND_AFTER).toUpperCase(Locale.ENGLISH);
return mapper(dbSession).selectProjectsByNameQuery(nameQueryForSql, includeModules);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
index 687ee46adde..f096156ed3c 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
@@ -164,4 +164,6 @@ public interface ComponentMapper {
List<KeyWithUuidDto> selectComponentKeysHavingIssuesToMerge(@Param("mergeBranchUuid") String mergeBranchUuid);
List<ProjectNclocDistributionDto> selectPrivateProjectsWithNcloc(@Param("organizationUuid") String organizationUuid);
+
+ List<ComponentWithModuleUuidDto> selectComponentsWithModuleUuidFromProjectKey(String projectKey);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentWithModuleUuidDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentWithModuleUuidDto.java
new file mode 100644
index 00000000000..3c2045f4873
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentWithModuleUuidDto.java
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+public class ComponentWithModuleUuidDto {
+ private final String uuid;
+ private final String moduleUuid;
+ private final String path;
+
+ public ComponentWithModuleUuidDto(String uuid, String moduleUuid, String path) {
+ this.uuid = uuid;
+ this.moduleUuid = moduleUuid;
+ this.path = path;
+ }
+
+ public String path() {
+ return path;
+ }
+
+ public String moduleUuid() {
+ return moduleUuid;
+ }
+
+ public String uuid() {
+ return uuid;
+ }
+}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
index 1f79ca8d1a3..77fd01b84ba 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
@@ -509,6 +509,15 @@
</if>
</where>
</select>
+
+ <select id="selectComponentsWithModuleUuidFromProjectKey" resultType="ComponentWithModuleUuid">
+ SELECT
+ p.uuid as uuid, p.module_uuid as moduleUuid, p.path as path
+ FROM
+ projects p
+ INNER JOIN
+ projects root ON root.uuid=p.project_uuid AND root.kee=#{projectKey,jdbcType=VARCHAR}
+ </select>
<select id="selectUuidsByKeyFromProjectKey" parameterType="string" resultType="KeyWithUuid">
SELECT
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
index c1839b702a2..76ae9566b0c 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
@@ -528,6 +528,34 @@ public class ComponentDaoTest {
}
@Test
+ public void select_components_with_module_dto() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto module = db.components().insertComponent(newModuleDto(project));
+ ComponentDto removedModule = db.components().insertComponent(newModuleDto(project).setEnabled(false));
+ ComponentDto subModule = db.components().insertComponent(newModuleDto(module));
+ ComponentDto removedSubModule = db.components().insertComponent(newModuleDto(module).setEnabled(false));
+ ComponentDto directory = db.components().insertComponent(newDirectory(subModule, "src"));
+ ComponentDto removedDirectory = db.components().insertComponent(newDirectory(subModule, "src2").setEnabled(false));
+ ComponentDto file = db.components().insertComponent(newFileDto(subModule, directory));
+ ComponentDto removedFile = db.components().insertComponent(newFileDto(subModule, directory).setEnabled(false));
+
+ // From root project
+ assertThat(underTest.selectComponentsWithModuleUuidFromProjectKey(dbSession, project.getDbKey()))
+ .extracting(ComponentWithModuleUuidDto::uuid)
+ .containsExactlyInAnyOrder(
+ project.uuid(),
+ module.uuid(),
+ removedModule.uuid(),
+ subModule.uuid(),
+ removedSubModule.uuid(),
+ directory.uuid(),
+ removedDirectory.uuid(),
+ file.uuid(),
+ removedFile.uuid()
+ );
+ }
+
+ @Test
public void select_all_modules_tree() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto removedProject = db.components().insertPrivateProject(p -> p.setEnabled(false));