Quellcode durchsuchen

SONAR-17706 Drop support for modules in WS

tags/10.0.0.68432
Duarte Meneses vor 1 Jahr
Ursprung
Commit
3d33985aa4
32 geänderte Dateien mit 92 neuen und 864 gelöschten Zeilen
  1. 0
    42
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPath.java
  2. 0
    2
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
  3. 2
    83
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ProjectTrackerBaseLazyInput.java
  4. 2
    14
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerBaseInputFactory.java
  5. 1
    4
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java
  6. 0
    53
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPathTest.java
  7. 1
    3
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java
  8. 2
    79
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ProjectTrackerBaseLazyInputTest.java
  9. 1
    4
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerBaseInputFactoryTest.java
  10. 4
    6
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java
  11. 2
    15
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
  12. 2
    17
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
  13. 0
    5
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
  14. 0
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
  15. 6
    65
      server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
  16. 0
    13
      server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
  17. 14
    38
      server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
  18. 0
    92
      server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java
  19. 2
    2
      server/sonar-server-common/src/main/java/org/sonar/server/view/index/ViewIndexer.java
  20. 0
    2
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/IssuesAction.java
  21. 5
    25
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/ProjectAction.java
  22. 9
    37
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/ProjectDataLoader.java
  23. 6
    33
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/batch/ProjectActionTest.java
  24. 4
    37
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java
  25. 2
    16
      sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java
  26. 0
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java
  27. 0
    52
      sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/input/MultiModuleProjectRepository.java
  28. 23
    1
      sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/input/ProjectRepositories.java
  29. 0
    45
      sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/input/SingleProjectRepository.java
  30. 2
    1
      sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
  31. 0
    68
      sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/input/MultiModuleProjectRepositoryTest.java
  32. 2
    7
      sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/input/ProjectRepositoriesTest.java

+ 0
- 42
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPath.java Datei anzeigen

/*
* SonarQube
* Copyright (C) 2009-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.component;

import java.util.Map;
import java.util.function.Supplier;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
import org.sonar.scanner.protocol.output.ScannerReport;

public class ReportModulesPath implements Supplier<Map<String, String>> {
private final BatchReportReader reader;
private Map<String, String> cache;

public ReportModulesPath(BatchReportReader reader) {
this.reader = reader;
}

public Map<String, String> get() {
if (cache == null) {
ScannerReport.Metadata metadata = reader.readMetadata();
cache = metadata.getModulesProjectRelativePathByKeyMap();
}
return cache;
}
}

+ 0
- 2
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java Datei anzeigen

import org.sonar.ce.task.projectanalysis.component.PreviousSourceHashRepositoryImpl; import org.sonar.ce.task.projectanalysis.component.PreviousSourceHashRepositoryImpl;
import org.sonar.ce.task.projectanalysis.component.ProjectPersister; import org.sonar.ce.task.projectanalysis.component.ProjectPersister;
import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids; import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues; import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderImpl; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderImpl;
import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl; import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl;
// File System // File System
new ComputationTempFolderProvider(), new ComputationTempFolderProvider(),


ReportModulesPath.class,
FileStatusesImpl.class, FileStatusesImpl.class,
new MetricModule(), new MetricModule(),



+ 2
- 83
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ProjectTrackerBaseLazyInput.java Datei anzeigen

*/ */
package org.sonar.ce.task.projectanalysis.issue; package org.sonar.ce.task.projectanalysis.issue;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.resources.Qualifiers;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.IssueChangeContext;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.issue.IssueFieldsSetter;

import static org.apache.commons.lang.StringUtils.trimToEmpty;
import static org.sonar.core.issue.IssueChangeContext.issueChangeContextByUserBuilder;


class ProjectTrackerBaseLazyInput extends BaseInputFactory.BaseLazyInput { class ProjectTrackerBaseLazyInput extends BaseInputFactory.BaseLazyInput {

private final AnalysisMetadataHolder analysisMetadataHolder;
private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues;
private final DbClient dbClient;
private final IssueFieldsSetter issueUpdater;
private final ComponentIssuesLoader issuesLoader; private final ComponentIssuesLoader issuesLoader;
private final ReportModulesPath reportModulesPath;


ProjectTrackerBaseLazyInput(AnalysisMetadataHolder analysisMetadataHolder, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, DbClient dbClient,
IssueFieldsSetter issueUpdater, ComponentIssuesLoader issuesLoader, ReportModulesPath reportModulesPath, Component component) {
ProjectTrackerBaseLazyInput(DbClient dbClient, ComponentIssuesLoader issuesLoader, Component component) {
super(dbClient, component, null); super(dbClient, component, null);
this.analysisMetadataHolder = analysisMetadataHolder;
this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues;
this.dbClient = dbClient;
this.issueUpdater = issueUpdater;
this.issuesLoader = issuesLoader; this.issuesLoader = issuesLoader;
this.reportModulesPath = reportModulesPath;
} }


@Override @Override
protected List<DefaultIssue> loadIssues() { protected List<DefaultIssue> loadIssues() {
List<DefaultIssue> result = new ArrayList<>();
try (DbSession dbSession = dbClient.openSession(false)) {
Set<String> dirOrModulesUuidsWithIssues = dbClient.issueDao().selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(dbSession, component.getUuid());
if (!dirOrModulesUuidsWithIssues.isEmpty()) {
Map<String, String> pathByModuleKey = reportModulesPath.get();
// Migrate issues that were previously on modules or directories to the root project
String branchKey = analysisMetadataHolder.isBranch() ? analysisMetadataHolder.getBranch().getName() : null;
String prKey = analysisMetadataHolder.isPullRequest() ? analysisMetadataHolder.getBranch().getPullRequestKey() : null;

Map<String, ComponentDto> modulesByUuid = dbClient.componentDao()
.selectProjectAndModulesFromProjectKey(dbSession, component.getKey(), true, branchKey, prKey)
.stream().collect(Collectors.toMap(ComponentDto::uuid, Function.identity()));
List<ComponentDto> dirOrModulesWithIssues = dbClient.componentDao().selectByUuids(dbSession, dirOrModulesUuidsWithIssues);
dirOrModulesWithIssues.forEach(c -> {
List<DefaultIssue> issuesOnModuleOrDir = issuesLoader.loadOpenIssues(c.uuid());
String moduleOrDirProjectRelativePath = c.qualifier().equals(Qualifiers.MODULE) ? buildModuleProjectRelativePath(pathByModuleKey, c)
: buildDirectoryProjectRelativePath(pathByModuleKey, c, modulesByUuid.get(c.moduleUuid()));
result.addAll(migrateIssuesToTheRoot(issuesOnModuleOrDir, moduleOrDirProjectRelativePath));
componentsWithUnprocessedIssues.remove(c.uuid());
});
}
result.addAll(issuesLoader.loadOpenIssues(effectiveUuid));
return result;
}
}

private static String buildDirectoryProjectRelativePath(Map<String, String> pathByModuleKey, ComponentDto c, ComponentDto parentModule) {
String moduleProjectRelativePath = buildModuleProjectRelativePath(pathByModuleKey, parentModule);
return Stream.of(moduleProjectRelativePath, c.path())
.map(StringUtils::trimToNull)
.filter(s -> s != null && !"/".equals(s))
.collect(Collectors.joining("/"));
}

private static String buildModuleProjectRelativePath(Map<String, String> pathByModuleKey, ComponentDto parentModule) {
return Optional.ofNullable(pathByModuleKey.get(parentModule.getKey()))
// If module is not in the scanner report, we can't guess the path accurately. Fallback on what we have in DB
.orElse(trimToEmpty(parentModule.path()));
}

private Collection<? extends DefaultIssue> migrateIssuesToTheRoot(List<DefaultIssue> issuesOnModule, String modulePath) {
for (DefaultIssue i : issuesOnModule) {
// changes the issue's component uuid, add a change and set issue as changed to enforce it is persisted to DB
IssueChangeContext context = issueChangeContextByUserBuilder(new Date(analysisMetadataHolder.getAnalysisDate()), null).build();
if (StringUtils.isNotBlank(modulePath)) {
issueUpdater.setMessage(i, "[" + modulePath + "] " + i.getMessage(), context);
}
issueUpdater.setIssueComponent(i, component.getUuid(), component.getKey(), context.date());
}
return issuesOnModule;
return issuesLoader.loadOpenIssues(effectiveUuid);
} }
} }

+ 2
- 14
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerBaseInputFactory.java Datei anzeigen

import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository;
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository.OriginalFile; import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository.OriginalFile;
import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Input; import org.sonar.core.issue.tracking.Input;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.server.issue.IssueFieldsSetter;


/** /**
* Factory of {@link Input} of base data for issue tracking. Data are lazy-loaded. * Factory of {@link Input} of base data for issue tracking. Data are lazy-loaded.
private final ComponentIssuesLoader issuesLoader; private final ComponentIssuesLoader issuesLoader;
private final DbClient dbClient; private final DbClient dbClient;
private final MovedFilesRepository movedFilesRepository; private final MovedFilesRepository movedFilesRepository;
private final ReportModulesPath reportModulesPath;
private final AnalysisMetadataHolder analysisMetadataHolder;
private final IssueFieldsSetter issueUpdater;
private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues;


public TrackerBaseInputFactory(ComponentIssuesLoader issuesLoader, DbClient dbClient, MovedFilesRepository movedFilesRepository, ReportModulesPath reportModulesPath,
AnalysisMetadataHolder analysisMetadataHolder, IssueFieldsSetter issueUpdater, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues) {
public TrackerBaseInputFactory(ComponentIssuesLoader issuesLoader, DbClient dbClient, MovedFilesRepository movedFilesRepository) {
this.issuesLoader = issuesLoader; this.issuesLoader = issuesLoader;
this.dbClient = dbClient; this.dbClient = dbClient;
this.movedFilesRepository = movedFilesRepository; this.movedFilesRepository = movedFilesRepository;
this.reportModulesPath = reportModulesPath;
this.analysisMetadataHolder = analysisMetadataHolder;
this.issueUpdater = issueUpdater;
this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues;
} }


public Input<DefaultIssue> create(Component component) { public Input<DefaultIssue> create(Component component) {
if (component.getType() == Component.Type.PROJECT) { if (component.getType() == Component.Type.PROJECT) {
return new ProjectTrackerBaseLazyInput(analysisMetadataHolder, componentsWithUnprocessedIssues, dbClient, issueUpdater, issuesLoader, reportModulesPath, component);
return new ProjectTrackerBaseLazyInput(dbClient, issuesLoader, component);
} }


if (component.getType() == Component.Type.DIRECTORY) { if (component.getType() == Component.Type.DIRECTORY) {

+ 1
- 4
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java Datei anzeigen

import org.sonar.ce.task.projectanalysis.component.ComponentUuidFactoryImpl; import org.sonar.ce.task.projectanalysis.component.ComponentUuidFactoryImpl;
import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolder; import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolder;
import org.sonar.ce.task.projectanalysis.component.ProjectAttributes; import org.sonar.ce.task.projectanalysis.component.ProjectAttributes;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.ce.task.step.ComputationStep; import org.sonar.ce.task.step.ComputationStep;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
private final BatchReportReader reportReader; private final BatchReportReader reportReader;
private final MutableTreeRootHolder treeRootHolder; private final MutableTreeRootHolder treeRootHolder;
private final MutableAnalysisMetadataHolder analysisMetadataHolder; private final MutableAnalysisMetadataHolder analysisMetadataHolder;
private final ReportModulesPath reportModulesPath;


public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, MutableTreeRootHolder treeRootHolder, public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, MutableTreeRootHolder treeRootHolder,
MutableAnalysisMetadataHolder analysisMetadataHolder, ReportModulesPath reportModulesPath) {
MutableAnalysisMetadataHolder analysisMetadataHolder) {
this.dbClient = dbClient; this.dbClient = dbClient;
this.reportReader = reportReader; this.reportReader = reportReader;
this.treeRootHolder = treeRootHolder; this.treeRootHolder = treeRootHolder;
this.analysisMetadataHolder = analysisMetadataHolder; this.analysisMetadataHolder = analysisMetadataHolder;
this.reportModulesPath = reportModulesPath;
} }


@Override @Override

+ 0
- 53
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPathTest.java Datei anzeigen

/*
* SonarQube
* Copyright (C) 2009-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.component;

import java.util.Map;
import org.junit.Test;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
import org.sonar.scanner.protocol.output.ScannerReport;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
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.mockito.internal.verification.VerificationModeFactory.times;

public class ReportModulesPathTest {
private BatchReportReader reader = mock(BatchReportReader.class);
private ReportModulesPath reportModulesPath = new ReportModulesPath(reader);

@Test
public void should_cache_report_data() {
when(reader.readMetadata()).thenReturn(ScannerReport.Metadata.newBuilder()
.putModulesProjectRelativePathByKey("module1", "path1")
.setRootComponentRef(1)
.build());
Map<String, String> pathByModuleKey = reportModulesPath.get();
assertThat(pathByModuleKey).containsExactly(entry("module1", "path1"));
pathByModuleKey = reportModulesPath.get();
assertThat(pathByModuleKey).containsExactly(entry("module1", "path1"));

verify(reader, times(1)).readMetadata();
verifyNoMoreInteractions(reader);
}
}

+ 1
- 3
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java Datei anzeigen

import org.sonar.ce.task.projectanalysis.component.FileStatuses; import org.sonar.ce.task.projectanalysis.component.FileStatuses;
import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids; import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids;
import org.sonar.ce.task.projectanalysis.component.ReportComponent; import org.sonar.ce.task.projectanalysis.component.ReportComponent;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitor; import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitor;
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository;
DbClient dbClient = dbTester.getDbClient(); DbClient dbClient = dbTester.getDbClient();
TrackerRawInputFactory rawInputFactory = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash, new CommonRuleEngineImpl(), issueFilter, TrackerRawInputFactory rawInputFactory = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash, new CommonRuleEngineImpl(), issueFilter,
ruleRepositoryRule, activeRulesHolder); ruleRepositoryRule, activeRulesHolder);
TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository, mock(ReportModulesPath.class), analysisMetadataHolder,
new IssueFieldsSetter(), mock(ComponentsWithUnprocessedIssues.class));
TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository);
TrackerTargetBranchInputFactory targetInputFactory = new TrackerTargetBranchInputFactory(issuesLoader, targetBranchComponentUuids, dbClient, movedFilesRepository); TrackerTargetBranchInputFactory targetInputFactory = new TrackerTargetBranchInputFactory(issuesLoader, targetBranchComponentUuids, dbClient, movedFilesRepository);
TrackerReferenceBranchInputFactory mergeInputFactory = new TrackerReferenceBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient); TrackerReferenceBranchInputFactory mergeInputFactory = new TrackerReferenceBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient);
ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository); ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository);

+ 2
- 79
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ProjectTrackerBaseLazyInputTest.java Datei anzeigen

*/ */
package org.sonar.ce.task.projectanalysis.issue; package org.sonar.ce.task.projectanalysis.issue;


import com.google.common.collect.ImmutableMap;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.ReportComponent; import org.sonar.ce.task.projectanalysis.component.ReportComponent;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule; import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule;
import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto; import org.sonar.db.issue.IssueDto;
import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleDto;
import org.sonar.server.issue.IssueFieldsSetter;


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple; import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.api.utils.DateUtils.parseDate; import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newFileDto;
private ComponentDto rootProjectDto; private ComponentDto rootProjectDto;
private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(), private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(),
System2.INSTANCE, mock(IssueChangesToDeleteRepository.class)); System2.INSTANCE, mock(IssueChangesToDeleteRepository.class));
private ReportModulesPath reportModulesPath;


@Before @Before
public void prepare() { public void prepare() {
ReportComponent rootProject = ReportComponent.builder(Component.Type.FILE, 1) ReportComponent rootProject = ReportComponent.builder(Component.Type.FILE, 1)
.setKey(rootProjectDto.getKey()) .setKey(rootProjectDto.getKey())
.setUuid(rootProjectDto.uuid()).build(); .setUuid(rootProjectDto.uuid()).build();
reportModulesPath = mock(ReportModulesPath.class);
underTest = new ProjectTrackerBaseLazyInput(analysisMetadataHolder, mock(ComponentsWithUnprocessedIssues.class), dbClient, new IssueFieldsSetter(), issuesLoader,
reportModulesPath, rootProject);
underTest = new ProjectTrackerBaseLazyInput(dbClient, issuesLoader, rootProject);
} }


@Test @Test
public void return_only_open_project_issues_if_no_modules_and_folders() {
public void return_only_open_project_issues_if_no_folders() {
ComponentDto file = dbTester.components().insertComponent(newFileDto(rootProjectDto)); ComponentDto file = dbTester.components().insertComponent(newFileDto(rootProjectDto));
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null)); IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null));
IssueDto closedIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("CLOSED").setResolution("FIXED")); IssueDto closedIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("CLOSED").setResolution("FIXED"));


assertThat(underTest.loadIssues()).extracting(DefaultIssue::key).containsOnly(openIssueOnProject.getKey()); assertThat(underTest.loadIssues()).extracting(DefaultIssue::key).containsOnly(openIssueOnProject.getKey());
} }

@Test
public void migrate_and_return_folder_issues_on_root_project() {
when(reportModulesPath.get()).thenReturn(Collections.emptyMap());
ComponentDto folder = dbTester.components().insertComponent(newDirectory(rootProjectDto, "src"));
ComponentDto file = dbTester.components().insertComponent(newFileDto(rootProjectDto));
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null));
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null));
IssueDto openIssue1OnFile = dbTester.issues().insert(rule, rootProjectDto, file, i -> i.setStatus("OPEN").setResolution(null));

assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage)
.containsExactlyInAnyOrder(
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()),
tuple(openIssueOnDir.getKey(), "[src] Issue on dir"));

}

@Test
public void migrate_and_return_module_and_folder_issues_on_module() {
ComponentDto module = dbTester.components().insertComponent(newModuleDto(rootProjectDto).setPath("moduleAInDb"));
when(reportModulesPath.get()).thenReturn(ImmutableMap.of(module.getKey(), "moduleAInReport"));
ComponentDto folder = dbTester.components().insertComponent(newDirectory(module, "src"));
ComponentDto file = dbTester.components().insertComponent(newFileDto(module));
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null));
IssueDto openIssueOnModule = dbTester.issues().insert(rule, rootProjectDto, module, i -> i.setStatus("OPEN").setMessage("Issue on module").setResolution(null));
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null));
IssueDto openIssue1OnFile = dbTester.issues().insert(rule, rootProjectDto, file, i -> i.setStatus("OPEN").setResolution(null));

assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage)
.containsExactlyInAnyOrder(
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()),
tuple(openIssueOnModule.getKey(), "[moduleAInReport] Issue on module"),
tuple(openIssueOnDir.getKey(), "[moduleAInReport/src] Issue on dir"));

}

@Test
public void use_db_path_if_module_missing_in_report() {
ComponentDto module = dbTester.components().insertComponent(newModuleDto(rootProjectDto).setPath("moduleAInDb"));
when(reportModulesPath.get()).thenReturn(Collections.emptyMap());
ComponentDto folder = dbTester.components().insertComponent(newDirectory(module, "src"));
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null));
IssueDto openIssueOnModule = dbTester.issues().insert(rule, rootProjectDto, module, i -> i.setStatus("OPEN").setMessage("Issue on module").setResolution(null));
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null));

assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage)
.containsExactlyInAnyOrder(
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()),
tuple(openIssueOnModule.getKey(), "[moduleAInDb] Issue on module"),
tuple(openIssueOnDir.getKey(), "[moduleAInDb/src] Issue on dir"));

}

@Test
public void empty_path_if_module_missing_in_report_and_db_and_for_slash_folder() {
ComponentDto module = dbTester.components().insertComponent(newModuleDto(rootProjectDto).setPath(null));
when(reportModulesPath.get()).thenReturn(Collections.emptyMap());
ComponentDto folder = dbTester.components().insertComponent(newDirectory(module, "/"));
IssueDto openIssueOnProject = dbTester.issues().insert(rule, rootProjectDto, rootProjectDto, i -> i.setStatus("OPEN").setResolution(null));
IssueDto openIssueOnModule = dbTester.issues().insert(rule, rootProjectDto, module, i -> i.setStatus("OPEN").setMessage("Issue on module").setResolution(null));
IssueDto openIssueOnDir = dbTester.issues().insert(rule, rootProjectDto, folder, i -> i.setStatus("OPEN").setMessage("Issue on dir").setResolution(null));

assertThat(underTest.loadIssues()).extracting(DefaultIssue::key, DefaultIssue::getMessage)
.containsExactlyInAnyOrder(
tuple(openIssueOnProject.getKey(), openIssueOnProject.getMessage()),
tuple(openIssueOnModule.getKey(), "Issue on module"),
tuple(openIssueOnDir.getKey(), "Issue on dir"));

}
} }

+ 1
- 4
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerBaseInputFactoryTest.java Datei anzeigen

import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.ReportComponent; import org.sonar.ce.task.projectanalysis.component.ReportComponent;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository; import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository;
import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Input; import org.sonar.core.issue.tracking.Input;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.source.FileSourceDao; import org.sonar.db.source.FileSourceDao;
import org.sonar.server.issue.IssueFieldsSetter;


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;


private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class); private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class);


private TrackerBaseInputFactory underTest = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository, mock(ReportModulesPath.class), analysisMetadataHolder,
new IssueFieldsSetter(), mock(ComponentsWithUnprocessedIssues.class));
private TrackerBaseInputFactory underTest = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository);


@Before @Before
public void setUp() { public void setUp() {

+ 4
- 6
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java Datei anzeigen

import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl; import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl;
import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule; import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
import org.sonar.ce.task.step.TestComputationStepContext; import org.sonar.ce.task.step.TestComputationStepContext;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbTester; import org.sonar.db.DbTester;
public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule();
@Rule @Rule
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
private ReportModulesPath reportModulesPath = new ReportModulesPath(reportReader);


private DbClient dbClient = dbTester.getDbClient(); private DbClient dbClient = dbTester.getDbClient();
private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath);
private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);


@Test @Test
public void fails_if_root_component_does_not_exist_in_reportReader() { public void fails_if_root_component_does_not_exist_in_reportReader() {
.setAnalysisDate(ANALYSIS_DATE) .setAnalysisDate(ANALYSIS_DATE)
.setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY))) .setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY)))
.setBranch(branch); .setBranch(branch);
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath);
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF)); reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1)); reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));


.setAnalysisDate(ANALYSIS_DATE) .setAnalysisDate(ANALYSIS_DATE)
.setProject(Project.from(projectDto)) .setProject(Project.from(projectDto))
.setBranch(branch); .setBranch(branch);
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath);
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
reportReader.putComponent(component(ROOT_REF, PROJECT, componentDto.getKey())); reportReader.putComponent(component(ROOT_REF, PROJECT, componentDto.getKey()));


underTest.execute(new TestComputationStepContext()); underTest.execute(new TestComputationStepContext());
@Test @Test
public void generate_keys_when_using_main_branch() { public void generate_keys_when_using_main_branch() {
setAnalysisMetadataHolder(); setAnalysisMetadataHolder();
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath);
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF)); reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1)); reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));



+ 2
- 15
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java Datei anzeigen

return mapper(session).selectSubProjectsByComponentUuids(uuids); return mapper(session).selectSubProjectsByComponentUuids(uuids);
} }


public List<ComponentDto> selectEnabledDescendantModules(DbSession session, String rootComponentUuid) {
return mapper(session).selectDescendantModules(rootComponentUuid, Scopes.PROJECT, true);
}

public List<FilePathWithHashDto> selectEnabledDescendantFiles(DbSession session, String rootComponentUuid) {
return mapper(session).selectDescendantFiles(rootComponentUuid, Scopes.FILE, true);
public List<ComponentDto> selectEnabledViewsFromRootView(DbSession session, String rootViewUuid) {
return mapper(session).selectEnabledViewsFromRootView(rootViewUuid);
} }


public List<FilePathWithHashDto> selectEnabledFilesFromProject(DbSession session, String rootComponentUuid) { public List<FilePathWithHashDto> selectEnabledFilesFromProject(DbSession session, String rootComponentUuid) {
return mapper(session).selectUuidsByKeyFromProjectKeyAndBranchOrPr(projectKey, null, pullrequest); return mapper(session).selectUuidsByKeyFromProjectKeyAndBranchOrPr(projectKey, null, pullrequest);
} }


/**
* If no branch or pull request is provided, returns components in the main branch
*/
public List<ComponentDto> selectProjectAndModulesFromProjectKey(DbSession session, String projectKey, boolean excludeDisabled,
@Nullable String branch, @Nullable String pullRequest) {
checkState(branch == null || pullRequest == null, "Can't set both branch and pull request");
return mapper(session).selectComponentsFromProjectKeyAndScope(projectKey, Scopes.PROJECT, excludeDisabled, branch, pullRequest);
}

public List<ComponentDto> selectByKeys(DbSession session, Collection<String> keys) { public List<ComponentDto> selectByKeys(DbSession session, Collection<String> keys) {
return selectByKeys(session, keys, null, null); return selectByKeys(session, keys, null, null);
} }

+ 2
- 17
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java Datei anzeigen

List<ComponentDto> selectProjects(); List<ComponentDto> selectProjects();


/** /**
* Return all descendant modules (including itself) from a given component uuid and scope
* Return all descendant views (including itself) from a given root view
*/ */
List<ComponentDto> selectDescendantModules(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope,
@Param(value = "excludeDisabled") boolean excludeDisabled);
List<ComponentDto> selectEnabledViewsFromRootView(@Param("rootViewUuid") String rootViewUuid);


/** /**
* Return all files from a given project uuid and scope * Return all files from a given project uuid and scope
*/ */
List<FilePathWithHashDto> selectEnabledFilesFromProject(@Param("projectUuid") String projectUuid); List<FilePathWithHashDto> selectEnabledFilesFromProject(@Param("projectUuid") String projectUuid);


/**
* Return all descendant files from a given module uuid and scope
*/
List<FilePathWithHashDto> selectDescendantFiles(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope,
@Param(value = "excludeDisabled") boolean excludeDisabled);

/** /**
* Return uuids and project uuids from list of qualifiers * Return uuids and project uuids from list of qualifiers
* <p/> * <p/>
*/ */
List<UuidWithBranchUuidDto> selectUuidsForQualifiers(@Param("qualifiers") String... qualifiers); List<UuidWithBranchUuidDto> selectUuidsForQualifiers(@Param("qualifiers") String... qualifiers);


/**
* Return components of a given scope of a project
*
* @param scope scope of components to return. If null, all components are returned
*/
List<ComponentDto> selectComponentsFromProjectKeyAndScope(@Param("projectKey") String projectKey, @Nullable @Param("scope") String scope,
@Param(value = "excludeDisabled") boolean excludeDisabled, @Nullable @Param("branch") String branch, @Nullable @Param("pullRequest") String pullRequest);

/** /**
* Return keys and UUIDs of all components belonging to a project * Return keys and UUIDs of all components belonging to a project
*/ */

+ 0
- 5
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java Datei anzeigen

import org.sonar.db.WildcardPosition; import org.sonar.db.WildcardPosition;
import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentDto;


import static java.util.Collections.emptyList;
import static org.sonar.db.DaoUtils.buildLikeValue; import static org.sonar.db.DaoUtils.buildLikeValue;
import static org.sonar.db.DatabaseUtils.executeLargeInputs; import static org.sonar.db.DatabaseUtils.executeLargeInputs;


return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid); return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid);
} }


public Set<String> selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(DbSession session, String projectUuid) {
return mapper(session).selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(projectUuid);
}

public List<IssueDto> selectNonClosedByComponentUuidExcludingExternalsAndSecurityHotspots(DbSession dbSession, String componentUuid) { public List<IssueDto> selectNonClosedByComponentUuidExcludingExternalsAndSecurityHotspots(DbSession dbSession, String componentUuid) {
return mapper(dbSession).selectNonClosedByComponentUuidExcludingExternals(componentUuid); return mapper(dbSession).selectNonClosedByComponentUuidExcludingExternals(componentUuid);
} }

+ 0
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java Datei anzeigen



Set<String> selectComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid); Set<String> selectComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid);


Set<String> selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid);

List<IssueDto> selectByKeys(List<String> keys); List<IssueDto> selectByKeys(List<String> keys);


Set<String> selectIssueKeysByComponentUuid(@Param("componentUuid") String componentUuid); Set<String> selectIssueKeysByComponentUuid(@Param("componentUuid") String componentUuid);

+ 6
- 65
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml Datei anzeigen

</foreach> </foreach>
</select> </select>


<select id="selectDescendantModules" parameterType="map" resultType="Component">
<select id="selectEnabledViewsFromRootView" parameterType="map" resultType="Component">
SELECT SELECT
<include refid="componentColumns"/> <include refid="componentColumns"/>
FROM components p FROM components p
<include refid="modulesTreeQuery"/>
</select>

<sql id="modulesTreeQuery">
INNER JOIN components module ON
module.branch_uuid = p.branch_uuid
and module.uuid = #{moduleUuid}
and module.scope='PRJ' AND module.enabled = ${_true}
where where
p.scope = #{scope,jdbcType=VARCHAR}
<if test="excludeDisabled">
and p.enabled = ${_true}
</if>
and
<choose>
<when test="_databaseId == 'mssql'">
p.module_uuid_path LIKE module.module_uuid_path + '%'
</when>
<otherwise>
p.module_uuid_path LIKE module.module_uuid_path || '%'
</otherwise>
</choose>
</sql>
p.enabled=${_true}
and p.scope='PRJ'
and p.qualifier in ('VW', 'SVW', 'APP')
and p.branch_uuid=#{rootViewUuid,jdbcType=VARCHAR}
</select>


<select id="selectEnabledFilesFromProject" parameterType="map" resultType="FilePathWithHash"> <select id="selectEnabledFilesFromProject" parameterType="map" resultType="FilePathWithHash">
SELECT SELECT
root.uuid=#{projectUuid,jdbcType=VARCHAR} root.uuid=#{projectUuid,jdbcType=VARCHAR}
</select> </select>


<select id="selectDescendantFiles" parameterType="map" resultType="FilePathWithHash">
SELECT
p.uuid,
p.path,
p.module_uuid as moduleUuid,
fs.src_hash as srcHash,
fs.revision
FROM components p
INNER JOIN file_sources fs ON
fs.file_uuid=p.uuid
<include refid="modulesTreeQuery"/>
</select>

<select id="selectProjects" resultType="Component"> <select id="selectProjects" resultType="Component">
select select
<include refid="componentColumns"/> <include refid="componentColumns"/>
and p.copy_component_uuid is not null and p.copy_component_uuid is not null
</select> </select>


<select id="selectComponentsFromProjectKeyAndScope" parameterType="map" resultType="Component">
SELECT
<include refid="componentColumns"/>
FROM components p
INNER JOIN components root ON root.uuid=p.branch_uuid AND root.kee=#{projectKey,jdbcType=VARCHAR}
<if test="branch != null || pullRequest != null">
INNER JOIN project_branches pb ON pb.uuid=p.branch_uuid
</if>
<where>
<if test="excludeDisabled">
p.enabled = ${_true}
</if>
<if test="scope != null">
AND p.scope=#{scope,jdbcType=VARCHAR}
</if>
<choose>
<when test="branch != null">
AND pb.kee=#{branch,jdbcType=VARCHAR} AND pb.branch_type = 'BRANCH'
</when>
<when test="pullRequest != null">
AND pb.kee=#{pullRequest,jdbcType=VARCHAR} AND pb.branch_type = 'PULL_REQUEST'
</when>
<otherwise>
AND root.main_branch_project_uuid is null
</otherwise>
</choose>
</where>
</select>

<select id="selectByBranchUuid" parameterType="map" resultType="Component"> <select id="selectByBranchUuid" parameterType="map" resultType="Component">
SELECT SELECT
<include refid="componentColumns"/> <include refid="componentColumns"/>

+ 0
- 13
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml Datei anzeigen

i.kee, ic.issue_change_creation_date desc i.kee, ic.issue_change_creation_date desc
</select> </select>


<select id="selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string">
select
distinct(i.component_uuid)
from issues i
inner join components p on
p.uuid = i.component_uuid
and p.enabled = ${_true}
where
i.project_uuid=#{projectUuid,jdbcType=VARCHAR}
and i.status &lt;&gt; 'CLOSED'
and (p.qualifier = 'DIR' OR p.qualifier = 'BRC')
</select>

<select id="selectComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string"> <select id="selectComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string">
select distinct(i.component_uuid) select distinct(i.component_uuid)
from issues i from issues i

+ 14
- 38
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java Datei anzeigen

} }


@Test @Test
public void select_enabled_module_files_tree_from_module() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto module = db.components().insertComponent(newModuleDto(project));
ComponentDto fileDirectlyOnModule = db.components().insertComponent(newFileDto(module));
FileSourceDto fileSourceDirectlyOnModule = db.fileSources().insertFileSource(fileDirectlyOnModule);
ComponentDto subModule = db.components().insertComponent(newModuleDto(module));
ComponentDto directory = db.components().insertComponent(newDirectory(subModule, "src"));
ComponentDto file = db.components().insertComponent(newFileDto(subModule, directory));
FileSourceDto fileSource = db.fileSources().insertFileSource(file);

// From root project
assertThat(underTest.selectEnabledDescendantFiles(dbSession, project.uuid()))
.extracting(FilePathWithHashDto::getUuid, FilePathWithHashDto::getModuleUuid, FilePathWithHashDto::getSrcHash, FilePathWithHashDto::getPath, FilePathWithHashDto::getRevision)
.containsExactlyInAnyOrder(
tuple(fileDirectlyOnModule.uuid(), module.uuid(), fileSourceDirectlyOnModule.getSrcHash(), fileDirectlyOnModule.path(), fileSourceDirectlyOnModule.getRevision()),
tuple(file.uuid(), subModule.uuid(), fileSource.getSrcHash(), file.path(), fileSource.getRevision()));

// From module
assertThat(underTest.selectEnabledDescendantFiles(dbSession, module.uuid()))
.extracting(FilePathWithHashDto::getUuid, FilePathWithHashDto::getModuleUuid, FilePathWithHashDto::getSrcHash, FilePathWithHashDto::getPath, FilePathWithHashDto::getRevision)
.containsExactlyInAnyOrder(
tuple(fileDirectlyOnModule.uuid(), module.uuid(), fileSourceDirectlyOnModule.getSrcHash(), fileDirectlyOnModule.path(), fileSourceDirectlyOnModule.getRevision()),
tuple(file.uuid(), subModule.uuid(), fileSource.getSrcHash(), file.path(), fileSource.getRevision()));

// From sub module
assertThat(underTest.selectEnabledDescendantFiles(dbSession, subModule.uuid()))
.extracting(FilePathWithHashDto::getUuid, FilePathWithHashDto::getModuleUuid, FilePathWithHashDto::getSrcHash, FilePathWithHashDto::getPath, FilePathWithHashDto::getRevision)
.containsExactlyInAnyOrder(
tuple(file.uuid(), subModule.uuid(), fileSource.getSrcHash(), file.path(), fileSource.getRevision()));

// From directory
assertThat(underTest.selectEnabledDescendantFiles(dbSession, directory.uuid())).isEmpty();

assertThat(underTest.selectEnabledDescendantFiles(dbSession, "unknown")).isEmpty();
}

@Test
public void select_enabled_module_files_tree_from_project() {
public void select_enabled_files_from_project() {
ComponentDto project = db.components().insertPrivateProject(); ComponentDto project = db.components().insertPrivateProject();
ComponentDto module = db.components().insertComponent(newModuleDto(project)); ComponentDto module = db.components().insertComponent(newModuleDto(project));
ComponentDto fileDirectlyOnModule = db.components().insertComponent(newFileDto(module)); ComponentDto fileDirectlyOnModule = db.components().insertComponent(newFileDto(module));
assertThat(underTest.selectProjectsFromView(dbSession, "Unknown", "Unknown")).isEmpty(); assertThat(underTest.selectProjectsFromView(dbSession, "Unknown", "Unknown")).isEmpty();
} }


@Test
public void select_enabled_views_from_root_view() {
ComponentDto rootPortfolio = db.components().insertPrivatePortfolio();
ComponentDto subPortfolio = db.components().insertSubView(rootPortfolio);
ComponentDto project = db.components().insertPrivateProject();
db.components().insertComponent(newProjectCopy(project, subPortfolio));

assertThat(underTest.selectEnabledViewsFromRootView(dbSession, rootPortfolio.uuid()))
.extracting(ComponentDto::uuid)
.containsOnly(rootPortfolio.uuid(), subPortfolio.uuid());
assertThat(underTest.selectEnabledViewsFromRootView(dbSession, project.uuid())).isEmpty();
}

@Test @Test
public void select_projects_from_view_should_escape_like_sensitive_characters() { public void select_projects_from_view_should_escape_like_sensitive_characters() {
ComponentDto project1 = db.components().insertPrivateProject(); ComponentDto project1 = db.components().insertPrivateProject();

+ 0
- 92
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java Datei anzeigen

assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne(); assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne();
} }


@Test
public void selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid() {
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), randomAlphabetic(12)))
.isEmpty();

ComponentDto project1 = db.components().insertPrivateProject();
ComponentDto module11 = db.components().insertComponent(newModuleDto(project1));
ComponentDto dir11 = db.components().insertComponent(newDirectory(module11, randomAlphabetic(10)));
ComponentDto dir12 = db.components().insertComponent(newDirectory(module11, randomAlphabetic(11)));
ComponentDto module12 = db.components().insertComponent(newModuleDto(project1));
ComponentDto dir13 = db.components().insertComponent(newDirectory(module12, randomAlphabetic(12)));
ComponentDto dir14 = db.components().insertComponent(newDirectory(project1, randomAlphabetic(13)));
ComponentDto file11 = db.components().insertComponent(newFileDto(project1));
ComponentDto application = db.components().insertPrivateApplication();
ComponentDto view = db.components().insertPublicPortfolio();
ComponentDto subview = db.components().insertSubView(view);
ComponentDto project2 = db.components().insertPublicProject();
ComponentDto module21 = db.components().insertComponent(newModuleDto(project2));
ComponentDto dir21 = db.components().insertComponent(newDirectory(project2, randomAlphabetic(15)));
ComponentDto file21 = db.components().insertComponent(newFileDto(project2));
List<ComponentDto> allcomponents = asList(project1, module11, dir11, dir12, module12, dir13, dir14, file11, application, view, subview, project2, module21, dir21, file21);
List<ComponentDto> allModuleOrDirs = asList(module11, dir11, dir12, module12, dir13, dir14, module21, dir21);

// no issues => always empty
allcomponents.stream()
.map(ComponentDto::uuid)
.forEach(uuid -> assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), uuid))
.isEmpty());

// return module or dir only if has issue with status different from CLOSED
allModuleOrDirs
.forEach(moduleOrDir -> {
String projectUuid = moduleOrDir.branchUuid();
// CLOSED issue => not returned
db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(moduleOrDir).setStatus(STATUS_CLOSED));
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
.isEmpty();

// status != CLOSED => returned
STATUSES.stream()
.filter(t -> !STATUS_CLOSED.equals(t))
.forEach(status -> {
IssueDto issue = db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(moduleOrDir).setStatus(status));
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
.containsOnly(moduleOrDir.uuid());

db.executeDdl("delete from issues where kee='" + issue.getKey() + "'");
db.commit();
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
.isEmpty();
});
});

// never return project, view, subview, app or file, whatever the issue status
Stream.of(project1, file11, application, view, subview, project2, file21)
.forEach(neitherModuleNorDir -> {
String projectUuid = neitherModuleNorDir.branchUuid();
STATUSES
.forEach(status -> {
db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(neitherModuleNorDir).setStatus(status));
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
.isEmpty();
});
});

// never return whatever the component if it is disabled
allcomponents
.forEach(component -> {
String projectUuid = component.branchUuid();

// issues for each status => returned if component is dir or module
STATUSES
.forEach(status -> db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(component).setStatus(status)));
if (allModuleOrDirs.contains(component)) {
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
.containsOnly(component.uuid());
} else {
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
.isEmpty();
}

// disable component and test again => not returned anymore
db.getDbClient().componentDao().update(db.getSession(), ComponentUpdateDto.copyFrom(component).setBEnabled(false).setBChanged(true), component.qualifier());
db.getDbClient().componentDao().applyBChangesForRootComponentUuid(db.getSession(), projectUuid);
db.commit();
assertThat(db.getDbClient().componentDao().selectByUuid(db.getSession(), component.uuid()).get().isEnabled())
.isFalse();
assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
.isEmpty();
});
}

@Test @Test
public void selectByKey_givenOneIssueNewOnReferenceBranch_selectOneIssueWithNewOnReferenceBranch() { public void selectByKey_givenOneIssueNewOnReferenceBranch_selectOneIssueWithNewOnReferenceBranch() {
prepareIssuesComponent(); prepareIssuesComponent();

+ 2
- 2
server/sonar-server-common/src/main/java/org/sonar/server/view/index/ViewIndexer.java Datei anzeigen

} }


/** /**
* Index a root view : it will load projects on each sub views and index it.
* Index a root view : it will a view and its subviews and index them.
* Used by the compute engine to reindex a root view. * Used by the compute engine to reindex a root view.
* <p/> * <p/>
* The views lookup cache will be cleared * The views lookup cache will be cleared
public void index(String rootViewUuid) { public void index(String rootViewUuid) {
try (DbSession dbSession = dbClient.openSession(false)) { try (DbSession dbSession = dbClient.openSession(false)) {
Map<String, String> viewAndProjectViewUuidMap = new HashMap<>(); Map<String, String> viewAndProjectViewUuidMap = new HashMap<>();
for (ComponentDto viewOrSubView : dbClient.componentDao().selectEnabledDescendantModules(dbSession, rootViewUuid)) {
for (ComponentDto viewOrSubView : dbClient.componentDao().selectEnabledViewsFromRootView(dbSession, rootViewUuid)) {
viewAndProjectViewUuidMap.put(viewOrSubView.uuid(), viewOrSubView.branchUuid()); viewAndProjectViewUuidMap.put(viewOrSubView.uuid(), viewOrSubView.branchUuid());
} }
index(dbSession, viewAndProjectViewUuidMap, true, Size.REGULAR); index(dbSession, viewAndProjectViewUuidMap, true, Size.REGULAR);

+ 0
- 2
server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/IssuesAction.java Datei anzeigen

*/ */
package org.sonar.server.batch; package org.sonar.server.batch;


import com.google.common.base.Splitter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;


private static final String PARAM_KEY = "key"; private static final String PARAM_KEY = "key";
private static final String PARAM_BRANCH = "branch"; private static final String PARAM_BRANCH = "branch";
private static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings();


private final DbClient dbClient; private final DbClient dbClient;
private final UserSession userSession; private final UserSession userSession;

+ 5
- 25
server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/ProjectAction.java Datei anzeigen

*/ */
package org.sonar.server.batch; package org.sonar.server.batch;


import com.google.common.collect.Maps;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
import org.sonar.scanner.protocol.input.FileData; import org.sonar.scanner.protocol.input.FileData;
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository;
import org.sonar.scanner.protocol.input.ProjectRepositories; import org.sonar.scanner.protocol.input.ProjectRepositories;
import org.sonar.scanner.protocol.input.SingleProjectRepository;
import org.sonarqube.ws.Batch.WsProjectResponse; import org.sonarqube.ws.Batch.WsProjectResponse;
import org.sonarqube.ws.Batch.WsProjectResponse.FileData.Builder; import org.sonarqube.ws.Batch.WsProjectResponse.FileData.Builder;


.setChangelog( .setChangelog(
new Change("7.6", String.format("The use of module keys in parameter '%s' is deprecated", PARAM_KEY)), new Change("7.6", String.format("The use of module keys in parameter '%s' is deprecated", PARAM_KEY)),
new Change("7.6", "Stop returning settings"), new Change("7.6", "Stop returning settings"),
new Change("7.7", "Stop supporting preview mode, removed timestamp and last analysis date"))
new Change("7.7", "Stop supporting preview mode, removed timestamp and last analysis date"),
new Change("10.0", String.format("No longer possible to use module keys with parameter '%s'", PARAM_KEY)))
.setInternal(true) .setInternal(true)
.setHandler(this); .setHandler(this);


action action
.createParam(PARAM_KEY) .createParam(PARAM_KEY)
.setRequired(true) .setRequired(true)
.setDescription("Project or module key")
.setDescription("Project key")
.setExampleValue(KEY_PROJECT_EXAMPLE_001); .setExampleValue(KEY_PROJECT_EXAMPLE_001);


action action


private static WsProjectResponse buildResponse(ProjectRepositories data) { private static WsProjectResponse buildResponse(ProjectRepositories data) {
WsProjectResponse.Builder response = WsProjectResponse.newBuilder(); WsProjectResponse.Builder response = WsProjectResponse.newBuilder();
if (data instanceof SingleProjectRepository singleProjectRepository) {
response.putAllFileDataByPath(buildFileDataByPath(singleProjectRepository));
} else {
response.putAllFileDataByModuleAndPath(buildFileDataByModuleAndPath((MultiModuleProjectRepository) data));
}

response.putAllFileDataByPath(buildFileDataByPath(data));
return response.build(); return response.build();
} }


private static Map<String, WsProjectResponse.FileDataByPath> buildFileDataByModuleAndPath(MultiModuleProjectRepository data) {
return data.repositoriesByModule().entrySet()
.stream()
.map(entry -> Maps.immutableEntry(entry.getKey(), buildFileDataByPath(entry.getValue().fileData())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

private static Map<String, WsProjectResponse.FileData> buildFileDataByPath(SingleProjectRepository data) {
private static Map<String, WsProjectResponse.FileData> buildFileDataByPath(ProjectRepositories data) {
return data.fileData().entrySet() return data.fileData().entrySet()
.stream() .stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> toFileDataResponse(e.getValue()))); .collect(Collectors.toMap(Map.Entry::getKey, e -> toFileDataResponse(e.getValue())));
} }


private static WsProjectResponse.FileDataByPath buildFileDataByPath(Map<String, FileData> fileDataByPath) {
WsProjectResponse.FileDataByPath.Builder response = WsProjectResponse.FileDataByPath.newBuilder();
fileDataByPath.forEach((key, value) -> response.putFileDataByPath(key, toFileDataResponse(value)));
return response.build();
}

private static WsProjectResponse.FileData toFileDataResponse(FileData fileData) { private static WsProjectResponse.FileData toFileDataResponse(FileData fileData) {
Builder fileDataBuilder = WsProjectResponse.FileData.newBuilder(); Builder fileDataBuilder = WsProjectResponse.FileData.newBuilder();
ofNullable(fileData.hash()).ifPresent(fileDataBuilder::setHash); ofNullable(fileData.hash()).ifPresent(fileDataBuilder::setHash);

+ 9
- 37
server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/ProjectDataLoader.java Datei anzeigen

package org.sonar.server.batch; package org.sonar.server.batch;


import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonar.api.server.ServerSide; import org.sonar.api.server.ServerSide;
import org.sonar.api.web.UserRole; import org.sonar.api.web.UserRole;
import org.sonar.db.component.FilePathWithHashDto; import org.sonar.db.component.FilePathWithHashDto;
import org.sonar.db.permission.GlobalPermission; import org.sonar.db.permission.GlobalPermission;
import org.sonar.scanner.protocol.input.FileData; import org.sonar.scanner.protocol.input.FileData;
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository;
import org.sonar.scanner.protocol.input.ProjectRepositories; import org.sonar.scanner.protocol.input.ProjectRepositories;
import org.sonar.scanner.protocol.input.SingleProjectRepository;
import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSession;
boolean hasScanPerm = userSession.hasComponentPermission(UserRole.SCAN, project) || boolean hasScanPerm = userSession.hasComponentPermission(UserRole.SCAN, project) ||
userSession.hasPermission(GlobalPermission.SCAN); userSession.hasPermission(GlobalPermission.SCAN);
checkPermission(hasScanPerm); checkPermission(hasScanPerm);
ComponentDto branchOrMainModule = (branch == null && pullRequest == null) ? project
ComponentDto branchComponent = (branch == null && pullRequest == null) ? project
: componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, projectKey, branch, pullRequest); : componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, projectKey, branch, pullRequest);


List<ComponentDto> modulesTree = dbClient.componentDao().selectEnabledDescendantModules(session, branchOrMainModule.uuid());

List<FilePathWithHashDto> files = searchFilesWithHashAndRevision(session, branchOrMainModule);

// MMF-365 we still have to support multi-module projects because it's not possible to transform from logical to
// physical structure for some multi-module projects
if (modulesTree.size() > 1) {
MultiModuleProjectRepository repository = new MultiModuleProjectRepository();
addFileDataPerModule(repository, modulesTree, files);
return repository;
} else {
SingleProjectRepository repository = new SingleProjectRepository();
addFileData(repository, files);
return repository;
}
List<FilePathWithHashDto> files = searchFilesWithHashAndRevision(session, branchComponent);
ProjectRepositories repository = new ProjectRepositories();
addFileData(repository, files);
return repository;
} }
} }


private List<FilePathWithHashDto> searchFilesWithHashAndRevision(DbSession session, @Nullable ComponentDto module) {
if (module == null) {
private List<FilePathWithHashDto> searchFilesWithHashAndRevision(DbSession session, @Nullable ComponentDto branchComponent) {
if (branchComponent == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
return module.isRootProject() ? dbClient.componentDao().selectEnabledFilesFromProject(session, module.uuid())
: dbClient.componentDao().selectEnabledDescendantFiles(session, module.uuid());
}

private static void addFileDataPerModule(MultiModuleProjectRepository data, List<ComponentDto> moduleChildren, List<FilePathWithHashDto> files) {
Map<String, String> moduleKeysByUuid = new HashMap<>();
for (ComponentDto module : moduleChildren) {
moduleKeysByUuid.put(module.uuid(), module.getKey());
}

for (FilePathWithHashDto file : files) {
FileData fileData = new FileData(file.getSrcHash(), file.getRevision());
data.addFileDataToModule(moduleKeysByUuid.get(file.getModuleUuid()), file.getPath(), fileData);
}
return dbClient.componentDao().selectEnabledFilesFromProject(session, branchComponent.uuid());
} }


private static void addFileData(SingleProjectRepository data, List<FilePathWithHashDto> files) {
private static void addFileData(ProjectRepositories data, List<FilePathWithHashDto> files) {
for (FilePathWithHashDto file : files) { for (FilePathWithHashDto file : files) {
FileData fileData = new FileData(file.getSrcHash(), file.getRevision()); FileData fileData = new FileData(file.getSrcHash(), file.getRevision());
data.addFileData(file.getPath(), fileData); data.addFileData(file.getPath(), fileData);

+ 6
- 33
server/sonar-webserver-webapi/src/test/java/org/sonar/server/batch/ProjectActionTest.java Datei anzeigen

import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.sonar.scanner.protocol.input.FileData; import org.sonar.scanner.protocol.input.FileData;
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository;
import org.sonar.scanner.protocol.input.ProjectRepositories; import org.sonar.scanner.protocol.input.ProjectRepositories;
import org.sonar.scanner.protocol.input.SingleProjectRepository;
import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester; import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Batch.WsProjectResponse; import org.sonarqube.ws.Batch.WsProjectResponse;
import static org.sonar.test.JsonAssert.assertJson; import static org.sonar.test.JsonAssert.assertJson;


public class ProjectActionTest { public class ProjectActionTest {

private ProjectDataLoader projectDataLoader = mock(ProjectDataLoader.class);
private WsActionTester ws = new WsActionTester(new ProjectAction(projectDataLoader));
private final ProjectDataLoader projectDataLoader = mock(ProjectDataLoader.class);
private final WsActionTester ws = new WsActionTester(new ProjectAction(projectDataLoader));


@Test @Test
public void project_referentials() { public void project_referentials() {
String projectKey = "org.codehaus.sonar:sonar"; String projectKey = "org.codehaus.sonar:sonar";


ProjectRepositories projectReferentials = mock(SingleProjectRepository.class);
ProjectRepositories projectReferentials = mock(ProjectRepositories.class);


ArgumentCaptor<ProjectDataQuery> queryArgumentCaptor = ArgumentCaptor.forClass(ProjectDataQuery.class); ArgumentCaptor<ProjectDataQuery> queryArgumentCaptor = ArgumentCaptor.forClass(ProjectDataQuery.class);
when(projectDataLoader.load(queryArgumentCaptor.capture())).thenReturn(projectReferentials); when(projectDataLoader.load(queryArgumentCaptor.capture())).thenReturn(projectReferentials);
public void do_not_fail_when_a_path_is_null() { public void do_not_fail_when_a_path_is_null() {
String projectKey = "org.codehaus.sonar:sonar"; String projectKey = "org.codehaus.sonar:sonar";


ProjectRepositories projectRepositories = new MultiModuleProjectRepository()
.addFileDataToModule("module-1", null, new FileData(null, null));
ProjectRepositories projectRepositories = new ProjectRepositories()
.addFileData(null, new FileData(null, null));
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories);


WsProjectResponse wsProjectResponse = ws.newRequest() WsProjectResponse wsProjectResponse = ws.newRequest()
public void use_new_file_structure_for_projects_without_submodules() { public void use_new_file_structure_for_projects_without_submodules() {
String projectKey = "org.codehaus.sonar:sonar"; String projectKey = "org.codehaus.sonar:sonar";


ProjectRepositories projectRepositories = new SingleProjectRepository()
ProjectRepositories projectRepositories = new ProjectRepositories()
.addFileData("src/main/java/SomeClass.java", new FileData("789456", "123456789")); .addFileData("src/main/java/SomeClass.java", new FileData("789456", "123456789"));
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories); when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories);


assertThat(wsProjectResponse.getFileDataByPathCount()).isOne(); assertThat(wsProjectResponse.getFileDataByPathCount()).isOne();
assertThat(wsProjectResponse.getFileDataByPathMap().get("src/main/java/SomeClass.java")).isNotNull(); assertThat(wsProjectResponse.getFileDataByPathMap().get("src/main/java/SomeClass.java")).isNotNull();
} }

@Test
public void use_old_file_structure_for_projects_with_submodules() {
String projectKey = "org.codehaus.sonar:sonar";

ProjectRepositories projectRepositories = new MultiModuleProjectRepository()
.addFileDataToModule("module-1", "src/main/java/SomeClass.java", new FileData("789456", "123456789"));
when(projectDataLoader.load(any(ProjectDataQuery.class))).thenReturn(projectRepositories);

WsProjectResponse wsProjectResponse = ws.newRequest()
.setParam("key", projectKey)
.setParam("profile", "Default")
.executeProtobuf(WsProjectResponse.class);

assertThat(wsProjectResponse.getFileDataByPathMap()).isEmpty();
assertThat(wsProjectResponse.getFileDataByModuleAndPathCount()).isOne();
WsProjectResponse.FileDataByPath moduleData = wsProjectResponse.getFileDataByModuleAndPathMap().get("module-1");
assertThat(moduleData).isNotNull();
assertThat(moduleData.getFileDataByPathCount()).isOne();
WsProjectResponse.FileData fileData = moduleData.getFileDataByPathMap().get("src/main/java/SomeClass.java");
assertThat(fileData).isNotNull();
assertThat(fileData.getHash()).isEqualTo("789456");
assertThat(fileData.getRevision()).isEqualTo("123456789");
}
} }

+ 4
- 37
server/sonar-webserver-webapi/src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java Datei anzeigen

import org.sonar.db.component.ResourceTypesRule; import org.sonar.db.component.ResourceTypesRule;
import org.sonar.db.source.FileSourceDto; import org.sonar.db.source.FileSourceDto;
import org.sonar.scanner.protocol.input.FileData; import org.sonar.scanner.protocol.input.FileData;
import org.sonar.scanner.protocol.input.MultiModuleProjectRepository;
import org.sonar.scanner.protocol.input.ProjectRepositories; import org.sonar.scanner.protocol.input.ProjectRepositories;
import org.sonar.scanner.protocol.input.SingleProjectRepository;
import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ForbiddenException;
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(file).setSrcHash("123456")); dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(file).setSrcHash("123456"));
db.commit(); db.commit();


ProjectRepositories ref = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey()));

assertTrue(ref instanceof SingleProjectRepository);
SingleProjectRepository singleProjectRepository = ((SingleProjectRepository) ref);
assertThat(singleProjectRepository.fileData()).hasSize(1);
FileData fileData = singleProjectRepository.fileDataByPath(file.path());
ProjectRepositories projectRepository = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey()));
assertThat(projectRepository.fileData()).hasSize(1);
FileData fileData = projectRepository.fileDataByPath(file.path());
assertThat(fileData).isNotNull(); assertThat(fileData).isNotNull();
assertThat(fileData.hash()).isEqualTo("123456"); assertThat(fileData.hash()).isEqualTo("123456");
} }


@Test
public void return_file_data_from_multi_modules() {
ComponentDto project = db.components().insertPrivateProject();
userSession.logIn().addProjectPermission(SCAN_EXECUTION, project);
ComponentDto module = db.components().insertComponent(newModuleDto(project));
// File on project
ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(projectFile).setSrcHash("123456"));
// File on module
ComponentDto moduleFile = db.components().insertComponent(newFileDto(module));
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(moduleFile).setSrcHash("789456"));
dbSession.commit();

ProjectRepositories ref = underTest.load(ProjectDataQuery.create().setProjectKey(project.getKey()));

assertTrue(ref instanceof MultiModuleProjectRepository);
MultiModuleProjectRepository repository = ((MultiModuleProjectRepository) ref);
assertThat(repository.fileData(project.getKey(), projectFile.path()).hash()).isEqualTo("123456");
assertThat(repository.fileData(module.getKey(), moduleFile.path()).hash()).isEqualTo("789456");
}

@Test @Test
public void return_file_data_from_branch() { public void return_file_data_from_branch() {
ComponentDto project = db.components().insertPrivateProject(); ComponentDto project = db.components().insertPrivateProject();
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
userSession.logIn().addProjectPermission(SCAN_EXECUTION, project); userSession.logIn().addProjectPermission(SCAN_EXECUTION, project);
ComponentDto moduleBranch = db.components().insertComponent(newModuleDto(branch));
// File on branch // File on branch
ComponentDto projectFile = db.components().insertComponent(newFileDto(branch)); ComponentDto projectFile = db.components().insertComponent(newFileDto(branch));
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(projectFile).setSrcHash("123456")); dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(projectFile).setSrcHash("123456"));
// File on moduleBranch branch
ComponentDto moduleFile = db.components().insertComponent(newFileDto(moduleBranch));
dbClient.fileSourceDao().insert(dbSession, newFileSourceDto(moduleFile).setSrcHash("789456"));
dbSession.commit(); dbSession.commit();


ProjectRepositories ref = underTest.load(ProjectDataQuery.create() ProjectRepositories ref = underTest.load(ProjectDataQuery.create()
.setProjectKey(project.getKey()) .setProjectKey(project.getKey())
.setBranch("my_branch")); .setBranch("my_branch"));


assertTrue(ref instanceof MultiModuleProjectRepository);
MultiModuleProjectRepository repository = ((MultiModuleProjectRepository) ref);
assertThat(repository.fileData(branch.getKey(), projectFile.path()).hash()).isEqualTo("123456");
assertThat(repository.fileData(moduleBranch.getKey(), moduleFile.path()).hash()).isEqualTo("789456");
assertThat(ref.fileDataByPath(projectFile.path()).hash()).isEqualTo("123456");
} }


@Test @Test

+ 2
- 16
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java Datei anzeigen



import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.LinkedList;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.sonar.api.batch.fs.internal.AbstractProjectOrModule; import org.sonar.api.batch.fs.internal.AbstractProjectOrModule;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.scm.ScmProvider; import org.sonar.api.batch.scm.ScmProvider;
import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Loggers;
.setUpdatedAt(pluginEntry.getValue().getUpdatedAt()).build()); .setUpdatedAt(pluginEntry.getValue().getUpdatedAt()).build());
} }


addModulesRelativePaths(builder);
addRelativePathFromScmRoot(builder);


writer.writeMetadata(builder.build()); writer.writeMetadata(builder.build());
} }


private void addModulesRelativePaths(ScannerReport.Metadata.Builder builder) {
LinkedList<DefaultInputModule> queue = new LinkedList<>();
queue.add(moduleHierarchy.root());

while (!queue.isEmpty()) {
DefaultInputModule module = queue.removeFirst();
queue.addAll(moduleHierarchy.children(module));
String relativePath = moduleHierarchy.relativePathToRoot(module);
if (relativePath != null) {
builder.putModulesProjectRelativePathByKey(module.key(), relativePath);
}
}

private void addRelativePathFromScmRoot(ScannerReport.Metadata.Builder builder) {
ScmProvider scmProvider = scmConfiguration.provider(); ScmProvider scmProvider = scmConfiguration.provider();
if (scmProvider == null) { if (scmProvider == null) {
return; return;

+ 0
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java Datei anzeigen

assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
assertThat(metadata.getNewCodeReferenceBranch()).isEqualTo("newCodeReference"); assertThat(metadata.getNewCodeReferenceBranch()).isEqualTo("newCodeReference");
assertThat(metadata.getProjectKey()).isEqualTo("root"); assertThat(metadata.getProjectKey()).isEqualTo("root");
assertThat(metadata.getModulesProjectRelativePathByKeyMap()).containsOnly(entry("module", "modulePath"), entry("root", ""));
assertThat(metadata.getProjectVersion()).isEmpty(); assertThat(metadata.getProjectVersion()).isEmpty();
assertThat(metadata.getNotAnalyzedFilesByLanguageCount()).isZero(); assertThat(metadata.getNotAnalyzedFilesByLanguageCount()).isZero();
assertThat(metadata.getQprofilesPerLanguageMap()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder() assertThat(metadata.getQprofilesPerLanguageMap()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder()

+ 0
- 52
sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/input/MultiModuleProjectRepository.java Datei anzeigen

/*
* SonarQube
* Copyright (C) 2009-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.scanner.protocol.input;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

public class MultiModuleProjectRepository implements ProjectRepositories {
private Map<String, SingleProjectRepository> repositoryPerModule = new HashMap<>();

public MultiModuleProjectRepository addFileDataToModule(String moduleKey, @Nullable String path, FileData fileData) {
if (path == null || (fileData.hash() == null && fileData.revision() == null)) {
return this;
}

SingleProjectRepository repository = repositoryPerModule.computeIfAbsent(moduleKey, k -> new SingleProjectRepository());
repository.addFileData(path, fileData);
return this;
}

public Map<String, SingleProjectRepository> repositoriesByModule() {
return repositoryPerModule;
}

@CheckForNull
public FileData fileData(String moduleKeyWithBranch, @Nullable String path) {
Optional<SingleProjectRepository> moduleRepository = Optional.ofNullable(repositoryPerModule.get(moduleKeyWithBranch));
return moduleRepository
.map(singleProjectRepository -> singleProjectRepository.fileDataByPath(path))
.orElse(null);
}
}

+ 23
- 1
sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/input/ProjectRepositories.java Datei anzeigen

*/ */
package org.sonar.scanner.protocol.input; package org.sonar.scanner.protocol.input;


public interface ProjectRepositories {
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;

public class ProjectRepositories {
private final Map<String, FileData> fileDataByPath = new HashMap<>();

public ProjectRepositories addFileData(@Nullable String path, FileData fileData) {
if (path == null || (fileData.hash() == null && fileData.revision() == null)) {
return this;
}

fileDataByPath.put(path, fileData);
return this;
}

public Map<String, FileData> fileData() {
return fileDataByPath;
}

public FileData fileDataByPath(@Nullable String path) {
return fileDataByPath.get(path);
}
} }

+ 0
- 45
sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/input/SingleProjectRepository.java Datei anzeigen

/*
* SonarQube
* Copyright (C) 2009-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.scanner.protocol.input;

import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;

public class SingleProjectRepository implements ProjectRepositories {
private Map<String, FileData> fileDataByPath = new HashMap<>();

public SingleProjectRepository addFileData(@Nullable String path, FileData fileData) {
if (path == null || (fileData.hash() == null && fileData.revision() == null)) {
return this;
}

fileDataByPath.put(path, fileData);
return this;
}

public Map<String, FileData> fileData() {
return fileDataByPath;
}

public FileData fileDataByPath(@Nullable String path) {
return fileDataByPath.get(path);
}
}

+ 2
- 1
sonar-scanner-protocol/src/main/protobuf/scanner_report.proto Datei anzeigen

string scm_revision_id = 13; string scm_revision_id = 13;


string pull_request_key = 14; string pull_request_key = 14;
map<string, string> modules_project_relative_path_by_key = 15;

reserved 15; // modules_project_relative_path_by_key (no longer used)


string projectVersion = 16; string projectVersion = 16;
string buildString = 17; string buildString = 17;

+ 0
- 68
sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/input/MultiModuleProjectRepositoryTest.java Datei anzeigen

/*
* SonarQube
* Copyright (C) 2009-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.scanner.protocol.input;

import org.junit.Before;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class MultiModuleProjectRepositoryTest {

private MultiModuleProjectRepository repository;

@Before
public void setUp() {
repository = new MultiModuleProjectRepository();
}

@Test
public void add_file_data_to_nodule() {
FileData fileData1 = new FileData("123", "456");
FileData fileData2 = new FileData("153", "6432");
FileData fileData3 = new FileData("987", "6343");

repository.addFileDataToModule("Module1", "/Abc.java", fileData1);
repository.addFileDataToModule("Module1", "/Xyz.java", fileData2);
repository.addFileDataToModule("Module2", "/Def.java", fileData3);

assertThat(repository.repositoriesByModule()).hasSize(2);
assertThat(repository.fileData("Module1", "/Xyz.java")).isEqualTo(fileData2);
assertThat(repository.fileData("Module2", "/Def.java")).isEqualTo(fileData3);
}

@Test
public void add_file_does_not_add_the_file_without_path() {
FileData fileData = new FileData("123", "456");

repository.addFileDataToModule("module1", null, fileData);

assertThat(repository.repositoriesByModule()).isEmpty();
}

@Test
public void add_file_does_not_add_the_file_without_revision_and_hash() {
FileData fileData = new FileData(null, null);

repository.addFileDataToModule("module2", "/Abc.java", fileData);

assertThat(repository.repositoriesByModule()).isEmpty();
}
}

sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/input/SingleProjectRepositoryTest.java → sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/input/ProjectRepositoriesTest.java Datei anzeigen



import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;


public class SingleProjectRepositoryTest {
private SingleProjectRepository repository;

@Before
public void setUp() {
repository = new SingleProjectRepository();
}
public class ProjectRepositoriesTest {
private final ProjectRepositories repository = new ProjectRepositories();


@Test @Test
public void add_file_data() { public void add_file_data() {

Laden…
Abbrechen
Speichern