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;
private final Branch branch;
@Nullable
private final SnapshotDto baseAnalysis;
+ private final IssueRelocationToRoot issueRelocationToRoot;
private ScannerReport.Component rootComponent;
private String scmBasePath;
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;
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<>();
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);
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());
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();
}
}
- return buildDirectory(currentPath, component, childComponents);
+ return buildDirectory(currentPath, childComponents);
}
private List<Component> buildChildren(Node node, String currentPath) {
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;
}
.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();
}
@CheckForNull
private static String computeScmPath(@Nullable String scmBasePath, String scmRelativePath) {
if (scmRelativePath.isEmpty()) {
- return null;
+ return scmBasePath;
}
if (scmBasePath == null) {
return scmRelativePath;
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;
}
/**
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;
ComponentsWithUnprocessedIssues.class,
ComponentIssuesRepositoryImpl.class,
IssueFilter.class,
+ IssueRelocationToRoot.class,
// common rules
CommonRuleEngineImpl.class,
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);
}
}
--- /dev/null
+/*
+ * 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();
+ }
+}
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;
this.issueFilter = issueFilter;
this.ruleRepository = ruleRepository;
this.activeRulesHolder = activeRulesHolder;
+ this.issueRelocationToRoot = issueRelocationToRoot;
}
public Input<DefaultIssue> create(Component component) {
}
}
+ if (component.getType() == Component.Type.PROJECT) {
+ // TODO apply filters?
+ issueRelocationToRoot.getMovedIssues().stream()
+ .map(i -> toIssue(getLineHashSequence(), i))
+ .forEach(result::add);
+ }
+
return result;
}
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;
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
reportReader::readComponent,
analysisMetadataHolder.getProject(),
analysisMetadataHolder.getBranch(),
- baseAnalysis);
+ baseAnalysis, issueRelocationToRoot);
String relativePathFromScmRoot = reportReader.readMetadata().getRelativePathFromScmRoot();
Component reportTreeRoot = builder.buildProject(reportProject, relativePathFromScmRoot);
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;
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();
assertThat(file.getUuid()).isEqualTo("generated_c1:src/js/Foo.js_uuid");
}
+ @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()
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) {
@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
--- /dev/null
+/*
+ * 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);
+ }
+
+}
@Test
public void return_existing_uuids() {
+ 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
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;
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 {
@Rule
public ExpectedException thrown = ExpectedException.none();
- @Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule();
-
@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
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
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
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;
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);
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) {
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);
List<KeyWithUuidDto> selectComponentKeysHavingIssuesToMerge(@Param("mergeBranchUuid") String mergeBranchUuid);
List<ProjectNclocDistributionDto> selectPrivateProjectsWithNcloc(@Param("organizationUuid") String organizationUuid);
+
+ List<ComponentWithModuleUuidDto> selectComponentsWithModuleUuidFromProjectKey(String projectKey);
}
--- /dev/null
+/*
+ * 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;
+ }
+}
</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
assertThat(underTest.selectEnabledDescendantModules(dbSession, "unknown")).isEmpty();
}
+ @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();