this.baseAnalysis = baseAnalysis;
}
- public Component buildProject(ScannerReport.Component project) {
- return buildComponent(project, project);
+ public Component buildProject(ScannerReport.Component project, String scmBasePath) {
+ return buildComponent(project, project, trimToNull(scmBasePath));
}
- private List<Component> buildChildren(ScannerReport.Component component, ScannerReport.Component parentModule) {
+ private List<Component> buildChildren(ScannerReport.Component component, ScannerReport.Component parentModule,
+ String projectScmPath) {
return component.getChildRefList()
.stream()
.map(scannerComponentSupplier::apply)
- .map(c -> buildComponent(c, parentModule))
+ .map(c -> buildComponent(c, parentModule, projectScmPath))
.collect(Collectors.toList());
}
- private ComponentImpl buildComponent(ScannerReport.Component component, ScannerReport.Component closestModule) {
+ private ComponentImpl buildComponent(ScannerReport.Component component, ScannerReport.Component closestModule,
+ @Nullable String scmBasePath) {
switch (component.getType()) {
case PROJECT:
String projectKey = keyGenerator.generateKey(component, null);
.setName(nameOfProject(component))
.setStatus(convertStatus(component.getStatus()))
.setDescription(trimToNull(component.getDescription()))
- .setReportAttributes(createAttributesBuilder(component)
+ .setReportAttributes(createAttributesBuilder(component, scmBasePath)
.setVersion(createProjectVersion(component))
.build())
- .addChildren(buildChildren(component, component))
+ .addChildren(buildChildren(component, component, scmBasePath))
.build();
case MODULE:
.setName(nameOfOthers(component, modulePublicKey))
.setStatus(convertStatus(component.getStatus()))
.setDescription(trimToNull(component.getDescription()))
- .setReportAttributes(createAttributesBuilder(component).build())
- .addChildren(buildChildren(component, component))
+ .setReportAttributes(createAttributesBuilder(component, scmBasePath).build())
+ .addChildren(buildChildren(component, component, scmBasePath))
.build();
case DIRECTORY:
.setName(nameOfOthers(component, publicKey))
.setStatus(convertStatus(component.getStatus()))
.setDescription(trimToNull(component.getDescription()))
- .setReportAttributes(createAttributesBuilder(component).build())
+ .setReportAttributes(createAttributesBuilder(component, scmBasePath).build())
.setFileAttributes(createFileAttributes(component))
- .addChildren(buildChildren(component, closestModule))
+ .addChildren(buildChildren(component, closestModule, scmBasePath))
.build();
default:
}
private static Component.Status convertStatus(FileStatus status) {
- switch(status) {
+ switch (status) {
case ADDED:
return Component.Status.ADDED;
case SAME:
return DEFAULT_PROJECT_VERSION;
}
- private static ReportAttributes.Builder createAttributesBuilder(ScannerReport.Component component) {
+ private static ReportAttributes.Builder createAttributesBuilder(ScannerReport.Component component, @Nullable String scmBasePath) {
return ReportAttributes.newBuilder(component.getRef())
.setVersion(trimToNull(component.getVersion()))
- .setPath(trimToNull(component.getPath()));
+ .setPath(trimToNull(component.getPath()))
+ .setScmPath(computeScmPath(scmBasePath, component.getProjectRelativePath()));
+ }
+
+ @CheckForNull
+ private static String computeScmPath(@Nullable String scmBasePath, String scmRelativePath) {
+ if (scmRelativePath.isEmpty()) {
+ return null;
+ }
+ if (scmBasePath == null) {
+ return scmRelativePath;
+ }
+ return scmBasePath + '/' + scmRelativePath;
}
@CheckForNull
*/
package org.sonar.server.computation.task.projectanalysis.component;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
private final String version;
@CheckForNull
private final String path;
+ @CheckForNull
+ private final String scmPath;
private ReportAttributes(Builder builder) {
this.ref = builder.ref;
this.version = builder.version;
this.path = builder.path;
+ this.scmPath = builder.scmPath;
}
public static Builder newBuilder(int ref) {
private String version;
@CheckForNull
private String path;
+ @CheckForNull
+ private String scmPath;
private Builder(int ref) {
this.ref = ref;
return this;
}
+ public Builder setScmPath(@Nullable String scmPath) {
+ this.scmPath = scmPath;
+ return this;
+ }
+
public ReportAttributes build() {
return new ReportAttributes(this);
}
return path;
}
+ /**
+ * The path of the component relative the SCM root the project is part of.
+ * <p>
+ * Can be {@link Optional#empty() empty} if project is not version controlled,
+ * otherwise should be non {@link Optional#isPresent() non empty} for all components.
+ */
+ public Optional<String> getScmPath() {
+ return Optional.ofNullable(scmPath);
+ }
+
@Override
public String toString() {
return "ReportAttributes{" +
"ref=" + ref +
", version='" + version + '\'' +
", path='" + path + '\'' +
+ ", scmPath='" + scmPath + '\'' +
'}';
}
}
reportReader::readComponent,
analysisMetadataHolder.getProject(),
baseAnalysis);
- Component project = builder.buildProject(reportProject);
+ String relativePathFromScmRoot = reportReader.readMetadata().getRelativePathFromScmRoot();
+ Component project = builder.buildProject(reportProject, relativePathFromScmRoot);
treeRootHolder.setRoot(project);
analysisMetadataHolder.setBaseAnalysis(toAnalysis(baseAnalysis));
import org.sonar.server.computation.task.projectanalysis.analysis.Project;
import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder;
+ ComponentKeys.createEffectiveKey(module.getKey(), component != null ? component.getPath() : null);
private static final Function<String, String> UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid";
private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, MODULE, DIRECTORY, FILE);
+ private static final String NO_SCM_BASE_PATH = "";
@Rule
public ExpectedException expectedException = ExpectedException.none();
}
@Test
- public void by_default_project_is_loaded_from_report() {
+ public void by_default_project_fields_are_loaded_from_report() {
String nameInReport = "the name";
String descriptionInReport = "the desc";
Component root = call(newBuilder()
assertThat(root.getDescription()).isNull();
}
+ @Test
+ public void project_scmPath_is_empty_if_scmBasePath_is_empty() {
+ Component root = call(newBuilder()
+ .setType(PROJECT)
+ .build(), NO_SCM_BASE_PATH);
+
+ assertThat(root.getReportAttributes().getScmPath()).isEmpty();
+ }
+
+ @Test
+ public void any_component_with_projectRelativePath_has_this_value_as_scmPath_if_scmBasePath_is_empty() {
+ String[] projectRelativePaths = {
+ randomAlphabetic(4),
+ randomAlphabetic(5),
+ randomAlphabetic(6),
+ randomAlphabetic(7)
+ };
+ ScannerReport.Component project = newBuilder()
+ .setType(PROJECT)
+ .setKey(projectInDb.getKey())
+ .setRef(1)
+ .addChildRef(2)
+ .setProjectRelativePath(projectRelativePaths[0])
+ .build();
+ scannerComponentProvider.add(newBuilder()
+ .setRef(2)
+ .setType(MODULE)
+ .setKey("M")
+ .setProjectRelativePath(projectRelativePaths[1])
+ .addChildRef(3));
+ scannerComponentProvider.add(newBuilder()
+ .setRef(3)
+ .setType(DIRECTORY)
+ .setPath("src/js")
+ .setProjectRelativePath(projectRelativePaths[2])
+ .addChildRef(4));
+ scannerComponentProvider.add(newBuilder()
+ .setRef(4)
+ .setType(FILE)
+ .setPath("src/js/Foo.js")
+ .setProjectRelativePath(projectRelativePaths[3])
+ .setLines(1));
+
+ Component root = call(project, NO_SCM_BASE_PATH);
+
+ assertThat(root.getReportAttributes().getScmPath())
+ .contains(projectRelativePaths[0]);
+ Component module = root.getChildren().iterator().next();
+ assertThat(module.getReportAttributes().getScmPath())
+ .contains(projectRelativePaths[1]);
+ Component directory = module.getChildren().iterator().next();
+ assertThat(directory.getReportAttributes().getScmPath())
+ .contains(projectRelativePaths[2]);
+ Component file = directory.getChildren().iterator().next();
+ assertThat(file.getReportAttributes().getScmPath())
+ .contains(projectRelativePaths[3]);
+ }
+
+ @Test
+ public void any_component_with_projectRelativePath_has_this_value_appended_to_scmBasePath_and_a_slash_as_scmPath_if_scmBasePath_is_not_empty() {
+ String[] projectRelativePaths = {
+ randomAlphabetic(4),
+ randomAlphabetic(5),
+ randomAlphabetic(6),
+ randomAlphabetic(7)
+ };
+ ScannerReport.Component project = newBuilder()
+ .setType(PROJECT)
+ .setKey(projectInDb.getKey())
+ .setRef(1)
+ .addChildRef(2)
+ .setProjectRelativePath(projectRelativePaths[0])
+ .build();
+ scannerComponentProvider.add(newBuilder()
+ .setRef(2)
+ .setType(MODULE)
+ .setKey("M")
+ .setProjectRelativePath(projectRelativePaths[1])
+ .addChildRef(3));
+ scannerComponentProvider.add(newBuilder()
+ .setRef(3)
+ .setType(DIRECTORY)
+ .setPath("src/js")
+ .setProjectRelativePath(projectRelativePaths[2])
+ .addChildRef(4));
+ scannerComponentProvider.add(newBuilder()
+ .setRef(4)
+ .setType(FILE)
+ .setPath("src/js/Foo.js")
+ .setProjectRelativePath(projectRelativePaths[3])
+ .setLines(1));
+ String scmBasePath = randomAlphabetic(10);
+
+ Component root = call(project, scmBasePath);
+
+ assertThat(root.getReportAttributes().getScmPath())
+ .contains(scmBasePath + "/" + projectRelativePaths[0]);
+ Component module = root.getChildren().iterator().next();
+ assertThat(module.getReportAttributes().getScmPath())
+ .contains(scmBasePath + "/" + projectRelativePaths[1]);
+ Component directory = module.getChildren().iterator().next();
+ assertThat(directory.getReportAttributes().getScmPath())
+ .contains(scmBasePath + "/" + projectRelativePaths[2]);
+ Component file = directory.getChildren().iterator().next();
+ assertThat(file.getReportAttributes().getScmPath())
+ .contains(scmBasePath + "/" + projectRelativePaths[3]);
+ }
+
@Test
public void keys_of_module_directory_and_file_are_generated() {
ScannerReport.Component project = newBuilder()
}
private Component call(ScannerReport.Component project) {
- return newUnderTest(null).buildProject(project);
+ return call(project, NO_SCM_BASE_PATH);
+ }
+
+ private Component call(ScannerReport.Component project, String scmBasePath) {
+ return newUnderTest(null).buildProject(project, scmBasePath);
}
private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis) {
- return newUnderTest(baseAnalysis).buildProject(project);
+ return call(project, baseAnalysis, NO_SCM_BASE_PATH);
+ }
+
+ private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis, String scmBasePath) {
+ return newUnderTest(baseAnalysis).buildProject(project, scmBasePath);
}
private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis) {