]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7278 fail report processing when component is not a project
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 17 May 2016 17:08:02 +0000 (19:08 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 18 May 2016 15:42:20 +0000 (17:42 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/ValidateProjectStepTest.java

index 89fbd82fbc4d0d1d199eb5cf4a222e339e4453d3..7c5e805808ef126724fee8f99b953607dfca3539 100644 (file)
@@ -21,12 +21,12 @@ package org.sonar.server.computation.step;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
-import javax.annotation.CheckForNull;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
 import org.sonar.api.utils.MessageException;
 import org.sonar.core.component.ComponentKeys;
 import org.sonar.db.DbClient;
@@ -43,6 +43,8 @@ import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler;
 import org.sonar.server.computation.component.TreeRootHolder;
 import org.sonar.server.computation.component.TypeAwareVisitorAdapter;
 
+import static com.google.common.collect.FluentIterable.from;
+import static java.lang.String.format;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
 import static org.sonar.db.component.ComponentDtoFunctions.toKey;
 
@@ -76,11 +78,11 @@ public class ValidateProjectStep implements ComputationStep {
   public void execute() {
     DbSession session = dbClient.openSession(false);
     try {
-      List<ComponentDto> baseModules = dbClient.componentDao().selectEnabledModulesFromProjectKey(session, treeRootHolder.getRoot().getKey());
-      Map<String, ComponentDto> baseModulesByKey = FluentIterable.from(baseModules).uniqueIndex(toKey());
-      ValidateProjectsVisitor visitor = new ValidateProjectsVisitor(session, dbClient.componentDao(),
-        baseModulesByKey);
-      new DepthTraversalTypeAwareCrawler(visitor).visit(treeRootHolder.getRoot());
+      Component root = treeRootHolder.getRoot();
+      List<ComponentDto> baseModules = dbClient.componentDao().selectEnabledModulesFromProjectKey(session, root.getKey());
+      Map<String, ComponentDto> baseModulesByKey = from(baseModules).uniqueIndex(toKey());
+      ValidateProjectsVisitor visitor = new ValidateProjectsVisitor(session, dbClient.componentDao(), baseModulesByKey);
+      new DepthTraversalTypeAwareCrawler(visitor).visit(root);
 
       if (!visitor.validationMessages.isEmpty()) {
         throw MessageException.of("Validation of project failed:\n  o " + MESSAGES_JOINER.join(visitor.validationMessages));
@@ -114,20 +116,30 @@ public class ValidateProjectStep implements ComputationStep {
     @Override
     public void visitProject(Component rawProject) {
       this.rawProject = rawProject;
+      String rawProjectKey = rawProject.getKey();
       validateBranch();
       validateBatchKey(rawProject);
 
-      String rawProjectKey = rawProject.getKey();
       Optional<ComponentDto> baseProject = loadBaseComponent(rawProjectKey);
+      validateRootIsProject(baseProject);
       validateProjectKey(baseProject, rawProjectKey);
       validateAnalysisDate(baseProject);
     }
 
+    private void validateRootIsProject(Optional<ComponentDto> baseProject) {
+      if (baseProject.isPresent()) {
+        ComponentDto componentDto = baseProject.get();
+        if (!Qualifiers.PROJECT.equals(componentDto.qualifier()) || !Scopes.PROJECT.equals(componentDto.scope())) {
+          validationMessages.add(format("Component (uuid=%s, key=%s) is not a project", rawProject.getUuid(), rawProject.getKey()));
+        }
+      }
+    }
+
     private void validateProjectKey(Optional<ComponentDto> baseProject, String rawProjectKey) {
       if (baseProject.isPresent() && !baseProject.get().projectUuid().equals(baseProject.get().uuid())) {
         // Project key is already used as a module of another project
         ComponentDto anotherBaseProject = componentDao.selectOrFailByUuid(session, baseProject.get().projectUuid());
-        validationMessages.add(String.format("The project \"%s\" is already defined in SonarQube but as a module of project \"%s\". "
+        validationMessages.add(format("The project \"%s\" is already defined in SonarQube but as a module of project \"%s\". "
           + "If you really want to stop directly analysing project \"%s\", please first delete it from SonarQube and then relaunch the analysis of project \"%s\".",
           rawProjectKey, anotherBaseProject.key(), anotherBaseProject.key(), rawProjectKey));
       }
@@ -139,7 +151,7 @@ public class ValidateProjectStep implements ComputationStep {
         long currentAnalysisDate = analysisMetadataHolder.getAnalysisDate();
         Long lastAnalysisDate = snapshotDto != null ? snapshotDto.getCreatedAt() : null;
         if (lastAnalysisDate != null && currentAnalysisDate <= snapshotDto.getCreatedAt()) {
-          validationMessages.add(String.format("Date of analysis cannot be older than the date of the last known analysis on this project. Value: \"%s\". " +
+          validationMessages.add(format("Date of analysis cannot be older than the date of the last known analysis on this project. Value: \"%s\". " +
             "Latest analysis: \"%s\". It's only possible to rebuild the past in a chronological order.",
             formatDateTime(new Date(currentAnalysisDate)), formatDateTime(new Date(lastAnalysisDate))));
         }
@@ -163,7 +175,7 @@ public class ValidateProjectStep implements ComputationStep {
     private void validateModuleIsNotAlreadyUsedAsProject(ComponentDto baseModule, String rawProjectKey, String rawModuleKey) {
       if (baseModule.projectUuid().equals(baseModule.uuid())) {
         // module is actually a project
-        validationMessages.add(String.format("The project \"%s\" is already defined in SonarQube but not as a module of project \"%s\". "
+        validationMessages.add(format("The project \"%s\" is already defined in SonarQube but not as a module of project \"%s\". "
           + "If you really want to stop directly analysing project \"%s\", please first delete it from SonarQube and then relaunch the analysis of project \"%s\".",
           rawModuleKey, rawProjectKey, rawModuleKey, rawProjectKey));
       }
@@ -172,26 +184,25 @@ public class ValidateProjectStep implements ComputationStep {
     private void validateModuleKeyIsNotAlreadyUsedInAnotherProject(ComponentDto baseModule, String rawModuleKey) {
       if (!baseModule.projectUuid().equals(baseModule.uuid()) && !baseModule.projectUuid().equals(rawProject.getUuid())) {
         ComponentDto projectModule = componentDao.selectOrFailByUuid(session, baseModule.projectUuid());
-        validationMessages.add(String.format("Module \"%s\" is already part of project \"%s\"", rawModuleKey, projectModule.key()));
+        validationMessages.add(format("Module \"%s\" is already part of project \"%s\"", rawModuleKey, projectModule.key()));
       }
     }
 
     private void validateBatchKey(Component rawComponent) {
       String batchKey = reportReader.readComponent(rawComponent.getReportAttributes().getRef()).getKey();
       if (!ComponentKeys.isValidModuleKey(batchKey)) {
-        validationMessages.add(String.format("\"%s\" is not a valid project or module key. "
+        validationMessages.add(format("\"%s\" is not a valid project or module key. "
           + "Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", batchKey));
       }
     }
 
-    @CheckForNull
     private void validateBranch() {
       String branch = analysisMetadataHolder.getBranch();
       if (branch == null) {
         return;
       }
       if (!ComponentKeys.isValidBranch(branch)) {
-        validationMessages.add(String.format("\"%s\" is not a valid branch name. "
+        validationMessages.add(format("\"%s\" is not a valid branch name. "
           + "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", branch));
       }
     }
index 6078a73df0630dbc35b1774db5806118ac4d3a43..e34dcdb7de1e0ffe35b0836598345706307c265d 100644 (file)
@@ -73,6 +73,26 @@ public class ValidateProjectStepTest {
     dbTester.truncateTables();
   }
 
+  @Test
+  public void fail_if_root_component_is_not_a_project_in_db() {
+    reportReader.putComponent(ScannerReport.Component.newBuilder()
+      .setRef(1)
+      .setType(ComponentType.PROJECT)
+      .setKey(PROJECT_KEY)
+      .build());
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY).build());
+
+    ComponentDto project = ComponentTesting.newView("ABCD").setKey(PROJECT_KEY);
+    dbClient.componentDao().insert(dbTester.getSession(), project);
+    dbTester.getSession().commit();
+
+    thrown.expect(MessageException.class);
+    thrown.expectMessage("Validation of project failed:\n" +
+        "  o Component (uuid=ABCD, key=PROJECT_KEY) is not a project");
+
+    underTest.execute();
+  }
+
   @Test
   public void not_fail_on_valid_branch() {
     analysisMetadataHolder.setBranch(DEFAULT_BRANCH);
@@ -88,10 +108,6 @@ public class ValidateProjectStepTest {
 
   @Test
   public void fail_on_invalid_branch() {
-    thrown.expect(MessageException.class);
-    thrown.expectMessage("Validation of project failed:\n" +
-      "  o \"bran#ch\" is not a valid branch name. Allowed characters are alphanumeric, '-', '_', '.' and '/'.");
-
     analysisMetadataHolder.setBranch("bran#ch");
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
@@ -100,6 +116,10 @@ public class ValidateProjectStepTest {
       .build());
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY + ":bran#ch").build());
 
+    thrown.expect(MessageException.class);
+    thrown.expectMessage("Validation of project failed:\n" +
+      "  o \"bran#ch\" is not a valid branch name. Allowed characters are alphanumeric, '-', '_', '.' and '/'.");
+
     underTest.execute();
   }
 
@@ -107,11 +127,6 @@ public class ValidateProjectStepTest {
   public void fail_on_invalid_key() {
     String invalidProjectKey = "Project\\Key";
 
-    thrown.expect(MessageException.class);
-    thrown.expectMessage("Validation of project failed:\n" +
-      "  o \"Project\\Key\" is not a valid project or module key. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.\n" +
-      "  o \"Module$Key\" is not a valid project or module key. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit");
-
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
       .setType(ComponentType.PROJECT)
@@ -127,17 +142,16 @@ public class ValidateProjectStepTest {
       ReportComponent.builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey("Module$Key").build())
       .build());
 
+    thrown.expect(MessageException.class);
+    thrown.expectMessage("Validation of project failed:\n" +
+      "  o \"Project\\Key\" is not a valid project or module key. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.\n" +
+      "  o \"Module$Key\" is not a valid project or module key. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit");
+
     underTest.execute();
   }
 
   @Test
   public void fail_if_module_key_is_already_used_as_project_key() {
-    thrown.expect(MessageException.class);
-    thrown.expectMessage("Validation of project failed:\n" +
-      "  o The project \"" + MODULE_KEY + "\" is already defined in SonarQube but not as a module of project \"" + PROJECT_KEY + "\". " +
-      "If you really want to stop directly analysing project \"" + MODULE_KEY + "\", please first delete it from SonarQube and then relaunch the analysis of project \""
-      + PROJECT_KEY + "\".");
-
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
       .setType(ComponentType.PROJECT)
@@ -158,16 +172,18 @@ public class ValidateProjectStepTest {
       ReportComponent.builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY).build())
       .build());
 
+    thrown.expect(MessageException.class);
+    thrown.expectMessage("Validation of project failed:\n" +
+      "  o The project \"" + MODULE_KEY + "\" is already defined in SonarQube but not as a module of project \"" + PROJECT_KEY + "\". " +
+      "If you really want to stop directly analysing project \"" + MODULE_KEY + "\", please first delete it from SonarQube and then relaunch the analysis of project \""
+      + PROJECT_KEY + "\".");
+
     underTest.execute();
   }
 
   @Test
   public void fail_if_module_key_already_exists_in_another_project() {
     String anotherProjectKey = "ANOTHER_PROJECT_KEY";
-    thrown.expect(MessageException.class);
-    thrown.expectMessage("Validation of project failed:\n" +
-      "  o Module \"" + MODULE_KEY + "\" is already part of project \"" + anotherProjectKey + "\"");
-
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
       .setType(ComponentType.PROJECT)
@@ -191,17 +207,16 @@ public class ValidateProjectStepTest {
       ReportComponent.builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY).build())
       .build());
 
+    thrown.expect(MessageException.class);
+    thrown.expectMessage("Validation of project failed:\n" +
+      "  o Module \"" + MODULE_KEY + "\" is already part of project \"" + anotherProjectKey + "\"");
+
     underTest.execute();
   }
 
   @Test
   public void fail_if_project_key_already_exists_as_module() {
     String anotherProjectKey = "ANOTHER_PROJECT_KEY";
-    thrown.expect(MessageException.class);
-    thrown.expectMessage("Validation of project failed:\n" +
-      "  o The project \"" + PROJECT_KEY + "\" is already defined in SonarQube but as a module of project \"" + anotherProjectKey + "\". " +
-      "If you really want to stop directly analysing project \"" + anotherProjectKey + "\", please first delete it from SonarQube and then relaunch the analysis of project \""
-      + PROJECT_KEY + "\".");
 
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
@@ -225,6 +240,13 @@ public class ValidateProjectStepTest {
       ReportComponent.builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY).build())
       .build());
 
+    thrown.expect(MessageException.class);
+    thrown.expectMessage("Validation of project failed:\n" +
+      "  o Component (uuid=ABCD, key=PROJECT_KEY) is not a project\n" +
+      "  o The project \"" + PROJECT_KEY + "\" is already defined in SonarQube but as a module of project \"" + anotherProjectKey + "\". " +
+      "If you really want to stop directly analysing project \"" + anotherProjectKey + "\", please first delete it from SonarQube and then relaunch the analysis of project \""
+      + PROJECT_KEY + "\".");
+
     underTest.execute();
   }
 
@@ -249,11 +271,6 @@ public class ValidateProjectStepTest {
 
   @Test
   public void fail_if_analysis_date_is_before_last_analysis() {
-    thrown.expect(MessageException.class);
-    thrown.expectMessage("Validation of project failed:");
-    thrown.expectMessage("Date of analysis cannot be older than the date of the last known analysis on this project. Value: ");
-    thrown.expectMessage("Latest analysis: ");
-
     analysisMetadataHolder.setAnalysisDate(DateUtils.parseDate("2015-01-01"));
 
     reportReader.putComponent(ScannerReport.Component.newBuilder()
@@ -270,6 +287,11 @@ public class ValidateProjectStepTest {
 
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY).build());
 
+    thrown.expect(MessageException.class);
+    thrown.expectMessage("Validation of project failed:");
+    thrown.expectMessage("Date of analysis cannot be older than the date of the last known analysis on this project. Value: ");
+    thrown.expectMessage("Latest analysis: ");
+
     underTest.execute();
   }
 }