]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18860 Refactor usage of main_branch_project_uuid to use branch is_main flag...
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Mon, 27 Mar 2023 20:44:00 +0000 (15:44 -0500)
committersonartech <sonartech@sonarsource.com>
Tue, 4 Apr 2023 20:03:16 +0000 (20:03 +0000)
30 files changed:
server/sonar-db-dao/src/it/java/org/sonar/db/project/ProjectDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml
server/sonar-server-common/src/it/java/org/sonar/server/setting/ProjectConfigurationLoaderImplIT.java
server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java
server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentCleanerServiceIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/SearchActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/BulkDeleteActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/ValuesActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/source/ws/ShowActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentFinder.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/ShowAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/TreeAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureComputerImpl.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/ValuesAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/source/ws/LinesAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/ui/ws/ComponentAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/setting/TestProjectConfigurationLoader.java

index 05958a69359a7d778a89f98e902d6f4c2f85f975..7f673703b611d47672cab505a12e5ca2cc6bfc31 100644 (file)
@@ -41,6 +41,7 @@ import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
 import org.sonar.db.audit.AuditPersister;
 import org.sonar.db.audit.NoOpAuditPersister;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.measure.LiveMeasureDto;
 import org.sonar.db.metric.MetricDto;
@@ -92,6 +93,14 @@ public class ProjectDaoIT {
     assertProject(projectByKee.get(), "projectName_p1", "projectKee_o1_p1", "uuid_o1_p1", "desc_p1", "tag1,tag2", false);
   }
 
+  @Test
+  public void select_by_branch_uuid() {
+    ProjectDto dto = createProject("o1", "p1");
+    projectDao.insert(db.getSession(), dto);
+    BranchDto branchDto = db.components().insertProjectBranch(dto);
+    assertThat(projectDao.selectByBranchUuid(db.getSession(), branchDto.getUuid()).get().getUuid()).isEqualTo(dto.getUuid());
+  }
+
   @Test
   public void select_projects() {
     ProjectDto dto1 = createProject("o1", "p1");
index 11f5c17c37f0f8a3eeab557800579ba5e4e3ca08..fb03394e5b1616f6931db850f62f82650e63495a 100644 (file)
@@ -83,6 +83,10 @@ public class ProjectDao implements Dao {
     return executeLargeInputs(keys, partition -> mapper(session).selectApplicationsByKeys(partition));
   }
 
+  public Optional<ProjectDto> selectByBranchUuid(DbSession dbSession, String branchUuid) {
+    return Optional.ofNullable(mapper(dbSession).selectByBranchUuid(branchUuid));
+  }
+
   public List<ProjectDto> selectProjects(DbSession session) {
     return mapper(session).selectProjects();
   }
index 3253c1578b3d7a96161df3b7a298f3457db306ea..928c98ec89903eabc39de0331af1c43e85f5d6c4 100644 (file)
@@ -60,6 +60,9 @@ public interface ProjectMapper {
 
   List<ProjectDto> selectApplicationsByKeys(@Param("kees") Collection<String> kees);
 
+  @CheckForNull
+  ProjectDto selectByBranchUuid(String branchUuid);
+
   List<String> selectAllProjectUuids();
 
   Set<String> selectProjectUuidsAssociatedToDefaultQualityProfileByLanguage(@Param("languageFilters") Set<String> languageFilters);
index c9c145aea33cca9a0b191a3eed39168693f7e239..57a30a3fa007c668b8713b5e1a0664fe96d74dad 100644 (file)
@@ -157,8 +157,8 @@ public class UserDao implements Dao {
     mapper(dbSession).clearHomepages("PROJECT", project.getUuid(), system2.now());
   }
 
-  public void cleanHomepage(DbSession dbSession, ComponentDto project) {
-    mapper(dbSession).clearHomepages("PROJECT", project.uuid(), system2.now());
+  public void cleanHomepage(DbSession dbSession, ComponentDto component) {
+    mapper(dbSession).clearHomepages("PROJECT", component.uuid(), system2.now());
   }
 
   public void cleanHomepage(DbSession dbSession, UserDto user) {
index 78e92a39d7a118cf2867c104490e50a6a89609a3..75d06fde93e9ca828520fdadfa0027b056d90e64 100644 (file)
     SELECT
       <include refid="componentColumns"/>
     FROM components p
+    left outer join project_branches pb on pb.uuid = p.branch_uuid
     where
       p.kee=#{key,jdbcType=VARCHAR}
-      and p.main_branch_project_uuid is null
-  </select>
+      and (pb.is_main = null or pb.is_main = ${_true}) </select>
 
   <select id="selectByKeyCaseInsensitive" parameterType="String" resultType="Component">
     SELECT
index baae55a33f228c1e7fd6b105bb5d5ae0258a01da..64679854e77b1218b4a49d9a2cf39048e47bf158 100644 (file)
       </foreach>
   </select>
 
+    <select id="selectByBranchUuid" parameterType="String" resultType="Project">
+    SELECT
+      <include refid="projectColumns"/>
+    FROM projects p inner join project_branches pb on pb.project_uuid = p.uuid
+    where pb.uuid=#{branchUuid,jdbcType=VARCHAR}
+  </select>
+
     <select id="selectProjects" resultType="Project">
     select
       <include refid="projectColumns"/>
index 99705d19784388b7ea9e5f3e12a21edc68757866..a038c4d7028259e920917cd038997cbcea35a759 100644 (file)
@@ -56,7 +56,7 @@ public class ProjectConfigurationLoaderImplIT {
     globalSettings.setProperty(MAIN_BRANCH_PROP_KEY, MAIN_BRANCH_PROP_VALUE);
 
     BranchDto mainBranch = insertBranch(MAIN_BRANCH_UUID, MAIN_BRANCH_UUID, true);
-    Configuration configuration = underTest.loadProjectConfiguration(db.getSession(), mainBranch);
+    Configuration configuration = underTest.loadBranchConfiguration(db.getSession(), mainBranch);
 
     assertThat(configuration.get(MAIN_BRANCH_PROP_KEY)).contains(MAIN_BRANCH_PROP_VALUE);
   }
@@ -72,8 +72,7 @@ public class ProjectConfigurationLoaderImplIT {
     db.properties().insertProperty(projectPropKey2, projectPropValue2, MAIN_BRANCH_UUID);
     BranchDto mainBranch = insertBranch(MAIN_BRANCH_UUID, MAIN_BRANCH_UUID, true);
 
-
-    Configuration configuration = underTest.loadProjectConfiguration(db.getSession(), mainBranch);
+    Configuration configuration = underTest.loadBranchConfiguration(db.getSession(), mainBranch);
 
     assertThat(configuration.get(GLOBAL_PROP_KEY)).contains(GLOBAL_PROP_VALUE);
     assertThat(configuration.get(projectPropKey1)).contains(projectPropValue1);
@@ -90,7 +89,7 @@ public class ProjectConfigurationLoaderImplIT {
     BranchDto mainBranch = insertBranch(MAIN_BRANCH_UUID, MAIN_BRANCH_UUID, true);
     BranchDto branch = insertBranch(BRANCH_UUID, MAIN_BRANCH_UUID, false);
 
-    Configuration configuration = underTest.loadProjectConfiguration(db.getSession(), branch);
+    Configuration configuration = underTest.loadBranchConfiguration(db.getSession(), branch);
 
     assertThat(configuration.get(GLOBAL_PROP_KEY)).contains(GLOBAL_PROP_VALUE);
     assertThat(configuration.get(MAIN_BRANCH_PROP_KEY)).contains(MAIN_BRANCH_PROP_VALUE);
index 69a220626c4eaab52dd3320c84788734b32901dd..d66c4974d272d74b09e49bc26ff27771b88941c2 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.setting;
 import org.sonar.api.config.Configuration;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.ComponentDto;
 
 public interface ProjectConfigurationLoader {
   /**
@@ -31,7 +30,8 @@ public interface ProjectConfigurationLoader {
    * Returns the applicable component configuration with most specific configuration overriding more global ones
    * (eg. global > project > branch).
    * <p>
-   * Any component is accepted but SQ only supports specific properties for projects and branches.
    */
-  Configuration loadProjectConfiguration(DbSession dbSession, BranchDto branch);
+  Configuration loadBranchConfiguration(DbSession dbSession, BranchDto branch);
+
+  Configuration loadProjectConfiguration(DbSession dbSession, String projectUuid);
 }
index 3f697a5611529206f5a304d2a1fa292577ed93a0..6b63510ef08401d390aeef27adfca5adbbcb12a3 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.setting;
 
 import java.util.List;
+import javax.annotation.Nullable;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.config.internal.Settings;
 import org.sonar.db.DbClient;
@@ -37,27 +38,34 @@ public class ProjectConfigurationLoaderImpl implements ProjectConfigurationLoade
   }
 
   @Override
-  public Configuration loadProjectConfiguration(DbSession dbSession, BranchDto branchDto) {
-    if (branchDto.isMain()) {
-      ChildSettings mainBranchSettings = loadMainBranchConfiguration(dbSession, branchDto.getUuid());
-      return mainBranchSettings.asConfiguration();
-    } else {
-      BranchDto mainBranch = dbClient.branchDao().selectMainBranchByProjectUuid(dbSession, branchDto.getProjectUuid())
-        .orElseThrow(() -> new IllegalStateException("Main branch not found for project: " + branchDto.getProjectUuid()));
-
-      ChildSettings mainBranchSettings = loadMainBranchConfiguration(dbSession, mainBranch.getUuid());
-      ChildSettings settings = new ChildSettings(mainBranchSettings);
-      dbClient.propertiesDao()
-        .selectComponentProperties(dbSession, branchDto.getUuid())
-        .forEach(property -> settings.setProperty(property.getKey(), property.getValue()));
-      return settings.asConfiguration();
+  public Configuration loadBranchConfiguration(DbSession dbSession, BranchDto branch) {
+    return loadProjectAndBranchConfiguration(dbSession, branch.getProjectUuid(), branch.getUuid());
+  }
+
+  @Override
+  public Configuration loadProjectConfiguration(DbSession dbSession, String projectUuid) {
+    return loadProjectAndBranchConfiguration(dbSession, projectUuid, null);
+  }
+
+  private Configuration loadProjectAndBranchConfiguration(DbSession dbSession, String projectUuid, @Nullable String branchUuid) {
+    ChildSettings projectSettings = internalLoadProjectConfiguration(dbSession, projectUuid);
+
+    if (branchUuid == null) {
+      return projectSettings.asConfiguration();
     }
+
+    ChildSettings settings = new ChildSettings(projectSettings);
+    dbClient.propertiesDao()
+      .selectComponentProperties(dbSession, branchUuid)
+      .forEach(property -> settings.setProperty(property.getKey(), property.getValue()));
+    return settings.asConfiguration();
   }
 
-  private ChildSettings loadMainBranchConfiguration(DbSession dbSession, String uuid) {
+  private ChildSettings internalLoadProjectConfiguration(DbSession dbSession, String uuid) {
     ChildSettings settings = new ChildSettings(globalSettings);
     List<PropertyDto> propertyDtos = dbClient.propertiesDao().selectComponentProperties(dbSession, uuid);
     propertyDtos.forEach(property -> settings.setProperty(property.getKey(), property.getValue()));
     return settings;
   }
+
 }
index 51fca49833f09431ead135fb601db6d16f8a8c8b..8a27c0aca4a277682ed2c6d73b3007297d67e3f9 100644 (file)
@@ -86,33 +86,26 @@ public class ComponentCleanerServiceIT {
   }
 
   @Test
-  public void delete_list_of_components_from_db() {
+  public void delete_component_from_db() {
     ComponentDto componentDto1 = db.components().insertPublicProject();
     ComponentDto componentDto2 = db.components().insertPublicProject();
-    ComponentDto componentDto3 = db.components().insertPublicProject();
 
     mockResourceTypeAsValidProject();
 
-    underTest.deleteComponents(dbSession, asList(componentDto1, componentDto2));
+    underTest.deleteComponent(dbSession, componentDto1);
     dbSession.commit();
 
     assertNotExists(componentDto1);
-    assertNotExists(componentDto2);
-    assertExists(componentDto3);
+    assertExists(componentDto2);
   }
 
   @Test
   public void fail_with_IAE_if_project_non_deletable() {
     ComponentDto componentDto1 = db.components().insertPublicProject();
-    ComponentDto componentDto2 = db.components().insertPublicProject();
-
     mockResourceTypeAsNonDeletable();
-
     dbSession.commit();
 
-    List<ComponentDto> componentDtos = asList(componentDto1, componentDto2);
-
-    assertThatThrownBy(() -> underTest.deleteComponents(dbSession, componentDtos))
+    assertThatThrownBy(() -> underTest.deleteComponent(dbSession, componentDto1))
       .withFailMessage("Only projects can be deleted")
       .isInstanceOf(IllegalArgumentException.class);
   }
@@ -187,7 +180,7 @@ public class ComponentCleanerServiceIT {
     dbClient.componentDao().insertOnMainBranch(dbSession, file);
     dbSession.commit();
 
-    assertThatThrownBy(() -> underTest.delete(dbSession, file))
+    assertThatThrownBy(() -> underTest.deleteComponent(dbSession, file))
       .isInstanceOf(IllegalArgumentException.class);
   }
 
index 7201714ab102c314689d93f898f3fca655f5af71..96933397589bd73b2c60a82df4c9f09a38749655 100644 (file)
@@ -337,7 +337,7 @@ public class SearchActionIT {
 
     assertThatThrownBy(request::execute)
       .isInstanceOf(NotFoundException.class)
-      .hasMessage("Component key '%s' not found", key);
+      .hasMessage("Project '%s' not found", key);
   }
 
   @Test
index 099d3fd16ae03cd633ad8e88c3c62878bcd08ade..be5985198c803aa64d8daba19169e54882a7b8af 100644 (file)
@@ -230,7 +230,7 @@ public class BulkDeleteActionIT {
       .setParam("projects", StringUtils.join(keys, ","))
       .execute();
 
-    verify(componentCleanerService, times(1_000)).delete(any(DbSession.class), any(ComponentDto.class));
+    verify(componentCleanerService, times(1_000)).deleteComponent(any(DbSession.class), any(ComponentDto.class));
     ArgumentCaptor<Set<Project>> projectsCaptor = ArgumentCaptor.forClass(Set.class);
     verify(projectLifeCycleListeners).onProjectsDeleted(projectsCaptor.capture());
     assertThat(projectsCaptor.getValue()).hasSize(1_000);
@@ -306,7 +306,7 @@ public class BulkDeleteActionIT {
 
   private void verifyComponentDeleted(ComponentDto... projects) {
     ArgumentCaptor<ComponentDto> argument = ArgumentCaptor.forClass(ComponentDto.class);
-    verify(componentCleanerService, times(projects.length)).delete(any(DbSession.class), argument.capture());
+    verify(componentCleanerService, times(projects.length)).deleteComponent(any(DbSession.class), argument.capture());
 
     for (ComponentDto project : projects) {
       assertThat(argument.getAllValues()).extracting(ComponentDto::uuid).contains(project.uuid());
index d80a7117d521f2ea8fd49083e49a6d46e411e629..1906ad4bde846344802d1fb4033edf393eca1f5c 100644 (file)
@@ -335,28 +335,28 @@ public class ValuesActionIT {
   }
 
   @Test
-  public void return_inherited_values_on_module() {
+  public void return_inherited_values_on_component() {
     logInAsProjectUser();
     ComponentDto file = db.components().insertComponent(newFileDto(project));
     definitions.addComponents(asList(
       PropertyDefinition.builder("defaultProperty").defaultValue("default").onQualifiers(PROJECT).build(),
       PropertyDefinition.builder("globalProperty").onQualifiers(PROJECT).build(),
       PropertyDefinition.builder("projectProperty").onQualifiers(PROJECT).build(),
-      PropertyDefinition.builder("moduleProperty").onQualifiers(PROJECT).build()));
+      PropertyDefinition.builder("componentProperty").onQualifiers(PROJECT).build()));
     db.properties().insertProperties(null, null, null, null,
       newGlobalPropertyDto().setKey("globalProperty").setValue("global"));
     db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
       newComponentPropertyDto(project).setKey("projectProperty").setValue("project"));
     db.properties().insertProperties(null, file.getKey(), file.name(), file.qualifier(),
-      newComponentPropertyDto(file).setKey("moduleProperty").setValue("module"));
+      newComponentPropertyDto(file).setKey("componentProperty").setValue("component"));
 
-    ValuesWsResponse result = executeRequestForComponentProperties(file, "defaultProperty", "globalProperty", "projectProperty", "moduleProperty");
+    ValuesWsResponse result = executeRequestForComponentProperties(file, "defaultProperty", "globalProperty", "projectProperty", "componentProperty");
 
     assertThat(result.getSettingsList()).hasSize(4);
     assertSetting(result.getSettings(0), "defaultProperty", "default", true);
     assertSetting(result.getSettings(1), "globalProperty", "global", true);
     assertSetting(result.getSettings(2), "projectProperty", "project", true);
-    assertSetting(result.getSettings(3), "moduleProperty", "module", false);
+    assertSetting(result.getSettings(3), "componentProperty", "component", false);
   }
 
   @Test
index ee50e82fe5e82117a9f7008acb9f35d5f4575bc8..efbbffdfaf6710cb3bb13fe1bde7f41718c0ceb2 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDao;
 import org.sonar.db.component.ComponentDao;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
@@ -54,6 +55,7 @@ public class ShowActionIT {
   private DbClient dbClient = mock(DbClient.class);
   private DbSession session = mock(DbSession.class);
   private ComponentDao componentDao = mock(ComponentDao.class);
+  private BranchDao branchDao = mock(BranchDao.class);
   private ComponentDto project = ComponentTesting.newPrivateProjectDto();
   private ComponentDto file = ComponentTesting.newFileDto(project);
   private ShowAction underTest = new ShowAction(sourceService, dbClient, userSessionRule,
@@ -63,6 +65,7 @@ public class ShowActionIT {
   @Before
   public void setUp() {
     when(dbClient.componentDao()).thenReturn(componentDao);
+    when(dbClient.branchDao()).thenReturn(branchDao);
     when(dbClient.openSession(false)).thenReturn(session);
   }
 
index 7557eaaa0df487a50cf8dd716a8d24c09d5ba0da..435bd6dc4e856920d543c3b9c3b38e1e2c90c0fb 100644 (file)
@@ -54,12 +54,6 @@ public class ComponentCleanerService {
     }
   }
 
-  public void deleteComponents(DbSession dbSession, List<ComponentDto> components) {
-    for (ComponentDto component : components) {
-      delete(dbSession, component);
-    }
-  }
-
   public void deleteBranch(DbSession dbSession, BranchDto branch) {
     dbClient.purgeDao().deleteBranch(dbSession, branch.getUuid());
     projectIndexers.commitAndIndexBranches(dbSession, singletonList(branch), PROJECT_DELETION);
@@ -78,12 +72,12 @@ public class ComponentCleanerService {
     projectIndexers.commitAndIndexProjects(dbSession, singletonList(application), PROJECT_DELETION);
   }
 
-  public void delete(DbSession dbSession, ComponentDto project) {
-    checkArgument(hasProjectScope(project) && isDeletable(project) && project.getMainBranchProjectUuid() == null, "Only projects can be deleted");
-    dbClient.purgeDao().deleteProject(dbSession, project.uuid(), project.qualifier(), project.name(), project.getKey());
-    dbClient.userDao().cleanHomepage(dbSession, project);
-    dbClient.userTokenDao().deleteByProjectKey(dbSession, project.getKey());
-    projectIndexers.commitAndIndexComponents(dbSession, singletonList(project), PROJECT_DELETION);
+  public void deleteComponent(DbSession dbSession, ComponentDto component) {
+    checkArgument(hasProjectScope(component) && isDeletable(component), "Only projects can be deleted");
+    dbClient.purgeDao().deleteProject(dbSession, component.uuid(), component.qualifier(), component.name(), component.getKey());
+    dbClient.userDao().cleanHomepage(dbSession, component);
+    dbClient.userTokenDao().deleteByProjectKey(dbSession, component.getKey());
+    projectIndexers.commitAndIndexComponents(dbSession, singletonList(component), PROJECT_DELETION);
   }
 
   private static boolean hasProjectScope(ComponentDto project) {
index 3ed37818c318a48e0355f4b1f79e60f59d9f2ad9..ef9394061c1612a28b701ad1ff8baa3d1804bd89 100644 (file)
@@ -113,6 +113,30 @@ public class ComponentFinder {
     return getBranchOrPullRequest(dbSession, project.getUuid(), project.getKey(), branchKey, pullRequestKey);
   }
 
+  public ProjectAndBranch getAppOrProjectAndBranch(DbSession dbSession, String projectKey, @Nullable String branchKey, @Nullable String pullRequestKey) {
+    ProjectDto projectOrApp = getProjectOrApplicationByKey(dbSession, projectKey);
+    BranchDto branch = getBranchOrPullRequest(dbSession, projectOrApp, branchKey, pullRequestKey);
+    return new ProjectAndBranch(projectOrApp, branch);
+  }
+
+  public static class ProjectAndBranch {
+    private final ProjectDto project;
+    private final BranchDto branch;
+
+    public ProjectAndBranch(ProjectDto project, BranchDto branch) {
+      this.project = project;
+      this.branch = branch;
+    }
+
+    public ProjectDto getProject() {
+      return project;
+    }
+
+    public BranchDto getBranch() {
+      return branch;
+    }
+  }
+
   public BranchDto getBranchOrPullRequest(DbSession dbSession, String projectUuid, String projectKey, @Nullable String branchKey, @Nullable String pullRequestKey) {
     if (branchKey != null) {
       return dbClient.branchDao().selectByBranchKey(dbSession, projectUuid, branchKey)
@@ -139,23 +163,22 @@ public class ComponentFinder {
   }
 
   private ComponentDto getByKey(DbSession dbSession, String key, String label) {
-    return checkComponent(dbClient.componentDao().selectByKey(dbSession, key), "%s key '%s' not found", label, key);
+    return checkComponent(dbSession, dbClient.componentDao().selectByKey(dbSession, key), "%s key '%s' not found", label, key);
   }
 
-  /**
-   * This method only returns components in main branches.
-   */
   public ComponentDto getByUuidFromMainBranch(DbSession dbSession, String uuid) {
     return getByUuidFromMainBranch(dbSession, uuid, LABEL_COMPONENT);
   }
 
   private ComponentDto getByUuidFromMainBranch(DbSession dbSession, String uuid, String label) {
-    return checkComponent(dbClient.componentDao().selectByUuid(dbSession, uuid), "%s id '%s' not found", label, uuid);
+    return checkComponent(dbSession, dbClient.componentDao().selectByUuid(dbSession, uuid), "%s id '%s' not found", label, uuid);
   }
 
-  private static ComponentDto checkComponent(Optional<ComponentDto> componentDto, String message, Object... messageArguments) {
-    if (componentDto.isPresent() && componentDto.get().isEnabled() && componentDto.get().getMainBranchProjectUuid() == null) {
-      return componentDto.get();
+  private ComponentDto checkComponent(DbSession session, Optional<ComponentDto> componentDto, String message, Object... messageArguments) {
+    if (componentDto.isPresent() && componentDto.get().isEnabled()) {
+      if (dbClient.branchDao().selectByUuid(session, componentDto.get().branchUuid()).map(BranchDto::isMain).orElse(true)) {
+        return componentDto.get();
+      }
     }
     throw new NotFoundException(format(message, messageArguments));
   }
index 45a3b0c428513d9d6fce5518174bade8c35d781e..8054a7745d05213a996bee294ac3d406d40b93e4 100644 (file)
@@ -66,7 +66,7 @@ class ComponentDtoToWsComponent {
   }
 
   public static Components.Component.Builder componentDtoToWsComponent(ComponentDto dto, @Nullable ProjectDto parentProjectDto,
-    @Nullable SnapshotDto lastAnalysis, @Nullable String branch, @Nullable String pullRequest) {
+    @Nullable SnapshotDto lastAnalysis, boolean isMainBranch, @Nullable String branch, @Nullable String pullRequest) {
     Components.Component.Builder wsComponent = Components.Component.newBuilder()
       .setKey(ComponentDto.removeBranchAndPullRequestFromKey(dto.getKey()))
       .setName(dto.name())
@@ -84,7 +84,7 @@ class ComponentDtoToWsComponent {
       });
     if (QUALIFIERS_WITH_VISIBILITY.contains(dto.qualifier())) {
       wsComponent.setVisibility(Visibility.getLabel(dto.isPrivate()));
-      if (Arrays.asList(Qualifiers.PROJECT, Qualifiers.APP).contains(dto.qualifier()) && dto.getMainBranchProjectUuid() != null && parentProjectDto != null) {
+      if (Arrays.asList(Qualifiers.PROJECT, Qualifiers.APP).contains(dto.qualifier()) && parentProjectDto != null && isMainBranch) {
         wsComponent.getTagsBuilder().addAllTags(parentProjectDto.getTags());
       }
     }
index fa8b9de46965b06b75299cd9cfd507d20d651bbe..277718f66efb66e9da8419da1da465570b8d51ed 100644 (file)
@@ -43,7 +43,6 @@ import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.Components;
 import org.sonarqube.ws.Components.ShowWsResponse;
 
-import static java.util.Optional.ofNullable;
 import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
 import static org.sonar.server.component.ws.ComponentDtoToWsComponent.projectOrAppToWsComponent;
 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
@@ -143,30 +142,37 @@ public class ShowAction implements ComponentsWsAction {
 
   private Components.Component.Builder toWsComponent(DbSession dbSession, ComponentDto component, @Nullable SnapshotDto lastAnalysis,
     Request request) {
+
+    // project or application
     if (isProjectOrApp(component)) {
       ProjectDto project = dbClient.projectDao().selectProjectOrAppByKey(dbSession, component.getKey())
         .orElseThrow(() -> new IllegalStateException("Project is in invalid state."));
       boolean needIssueSync = needIssueSync(dbSession, component, project);
-      return projectOrAppToWsComponent(project, lastAnalysis)
+      return projectOrAppToWsComponent(project, lastAnalysis).setNeedIssueSync(needIssueSync);
+    }
+
+    // parent project can an application. For components in portfolios, it will be null
+    ProjectDto parentProject = dbClient.projectDao().selectByBranchUuid(dbSession, component.branchUuid()).orElse(null);
+    boolean needIssueSync = needIssueSync(dbSession, component, parentProject);
+
+    // if this is a project calculated in a portfolio or app, we need to include the original branch name (if any)
+    if (component.getCopyComponentUuid() != null) {
+      String branch = dbClient.branchDao().selectByUuid(dbSession, component.getCopyComponentUuid())
+        .filter(b -> !b.isMain())
+        .map(BranchDto::getKey)
+        .orElse(null);
+      return componentDtoToWsComponent(component, parentProject, lastAnalysis, true, branch, null)
+        .setNeedIssueSync(needIssueSync);
+    }
+
+    // branch won't exist for portfolios
+    Optional<BranchDto> branchDto = dbClient.branchDao().selectByUuid(dbSession, component.branchUuid());
+    if (branchDto.isPresent() && !branchDto.get().isMain()) {
+      return componentDtoToWsComponent(component, parentProject, lastAnalysis, false, request.branch, request.pullRequest)
         .setNeedIssueSync(needIssueSync);
     } else {
-      Optional<ProjectDto> parentProject = dbClient.projectDao().selectByUuid(dbSession,
-        ofNullable(component.getMainBranchProjectUuid()).orElse(component.branchUuid()));
-      boolean needIssueSync = needIssueSync(dbSession, component, parentProject.orElse(null));
-      if (component.getCopyComponentUuid() != null) {
-        String branch = dbClient.branchDao().selectByUuid(dbSession, component.getCopyComponentUuid())
-          .filter(b -> !b.isMain())
-          .map(BranchDto::getKey)
-          .orElse(null);
-        return componentDtoToWsComponent(component, parentProject.orElse(null), lastAnalysis, branch, null)
-          .setNeedIssueSync(needIssueSync);
-      } else if (component.getMainBranchProjectUuid() != null) {
-        return componentDtoToWsComponent(component, parentProject.orElse(null), lastAnalysis, request.branch, request.pullRequest)
-          .setNeedIssueSync(needIssueSync);
-      } else {
-        return componentDtoToWsComponent(component, parentProject.orElse(null), lastAnalysis, null, null)
-          .setNeedIssueSync(needIssueSync);
-      }
+      return componentDtoToWsComponent(component, parentProject, lastAnalysis, true, null, null)
+        .setNeedIssueSync(needIssueSync);
     }
   }
 
index 1cbba6819d98478f8594eb3568d2651a2ac09225..1f786a346cba97bac65b4db3b00573c28a3826c6 100644 (file)
@@ -233,33 +233,33 @@ public class TreeAction implements ComponentsWsAction {
       .filter(b -> !b.isMain())
       .collect(Collectors.toMap(BranchDto::getUuid, BranchDto::getBranchKey));
 
-    response.setBaseComponent(toWsComponent(dbSession, baseComponent, referenceComponentsByUuid, branchKeyByReferenceUuid, request));
+    boolean isMainBranch = dbClient.branchDao().selectByUuid(dbSession, baseComponent.branchUuid()).map(BranchDto::isMain).orElse(true);
+    response.setBaseComponent(toWsComponent(dbSession, baseComponent, isMainBranch, referenceComponentsByUuid, branchKeyByReferenceUuid, request));
     for (ComponentDto dto : components) {
-      response.addComponents(toWsComponent(dbSession, dto, referenceComponentsByUuid, branchKeyByReferenceUuid, request));
+      response.addComponents(toWsComponent(dbSession, dto, isMainBranch, referenceComponentsByUuid, branchKeyByReferenceUuid, request));
     }
 
     return response.build();
   }
 
-  private Components.Component.Builder toWsComponent(DbSession dbSession, ComponentDto component,
+  private Components.Component.Builder toWsComponent(DbSession dbSession, ComponentDto component, boolean isMainBranch,
     Map<String, ComponentDto> referenceComponentsByUuid, Map<String, String> branchKeyByReferenceUuid, Request request) {
 
     ComponentDto referenceComponent = referenceComponentsByUuid.get(component.getCopyComponentUuid());
 
     Components.Component.Builder wsComponent;
-    if (component.getMainBranchProjectUuid() == null && component.isRootProject() && PROJECT_OR_APP_QUALIFIERS.contains(component.qualifier())) {
+    if (isMainBranch && component.isRootProject() && PROJECT_OR_APP_QUALIFIERS.contains(component.qualifier())) {
       ProjectDto projectDto = componentFinder.getProjectOrApplicationByKey(dbSession, component.getKey());
       wsComponent = projectOrAppToWsComponent(projectDto, null);
     } else {
-      Optional<ProjectDto> parentProject = dbClient.projectDao().selectByUuid(dbSession,
-        ofNullable(component.getMainBranchProjectUuid()).orElse(component.branchUuid()));
+      ProjectDto parentProject = dbClient.projectDao().selectByBranchUuid(dbSession, component.branchUuid()).orElse(null);
 
       if (referenceComponent != null) {
-        wsComponent = componentDtoToWsComponent(component, parentProject.orElse(null), null, branchKeyByReferenceUuid.get(referenceComponent.uuid()), null);
-      } else if (component.getMainBranchProjectUuid() != null) {
-        wsComponent = componentDtoToWsComponent(component, parentProject.orElse(null), null, request.branch, request.pullRequest);
+        wsComponent = componentDtoToWsComponent(component, parentProject, null, false, branchKeyByReferenceUuid.get(referenceComponent.uuid()), null);
+      } else if (!isMainBranch) {
+        wsComponent = componentDtoToWsComponent(component, parentProject, null, false, request.branch, request.pullRequest);
       } else {
-        wsComponent = componentDtoToWsComponent(component, parentProject.orElse(null), null, null, null);
+        wsComponent = componentDtoToWsComponent(component, parentProject, null, true, null, null);
       }
     }
 
index 5ec0692777ab58aa87550b01ec180e36b50cf4f8..31b961e38e0379d7344953526b47482ac097da03 100644 (file)
@@ -30,8 +30,8 @@ import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.core.util.Uuids;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
 import org.sonar.db.issue.IssueDto;
+import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.issue.IssueFieldsSetter;
 import org.sonar.server.issue.ws.IssueUpdater;
@@ -135,11 +135,11 @@ public class AssignAction implements HotspotsWsAction {
     return checkFound(dbClient.userDao().selectActiveUserByLogin(dbSession, assignee), "Unknown user: %s", assignee);
   }
 
-  private void checkAssigneeProjectPermission(DbSession dbSession, UserDto assignee, String issueProjectUuid) {
-    ComponentDto componentDto = checkFoundWithOptional(dbClient.componentDao().selectByUuid(dbSession, issueProjectUuid),
-      "Could not find project for issue");
-    String mainProjectUuid = componentDto.getMainBranchProjectUuid() == null ? componentDto.uuid() : componentDto.getMainBranchProjectUuid();
-    if (componentDto.isPrivate() && !hasProjectPermission(dbSession, assignee.getUuid(), mainProjectUuid)) {
+  private void checkAssigneeProjectPermission(DbSession dbSession, UserDto assignee, String issueBranchUuid) {
+    ProjectDto project = checkFoundWithOptional(dbClient.projectDao().selectByBranchUuid(dbSession, issueBranchUuid),
+      "Could not find branch for issue");
+
+    if (project.isPrivate() && !hasProjectPermission(dbSession, assignee.getUuid(), project.getUuid())) {
       throw new IllegalArgumentException(String.format("Provided user with login '%s' does not have 'Browse' permission to project", assignee.getLogin()));
     }
   }
index 590d9bd5ce0fb7bd526a3aa9b14e31ca2d89362e..e7edae1e7f714e094cfc23f0cb277ecb512c3a9d 100644 (file)
@@ -38,7 +38,6 @@ import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.search.SearchHit;
 import org.jetbrains.annotations.NotNull;
 import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Scopes;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ws.Change;
@@ -57,6 +56,7 @@ import org.sonar.db.project.ProjectDto;
 import org.sonar.db.protobuf.DbIssues;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.ComponentFinder.ProjectAndBranch;
 import org.sonar.server.es.SearchOptions;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.issue.TextRangeResponseFormatter;
@@ -70,7 +70,6 @@ import org.sonarqube.ws.Common;
 import org.sonarqube.ws.Hotspots;
 import org.sonarqube.ws.Hotspots.SearchWsResponse;
 
-import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Strings.isNullOrEmpty;
 import static java.lang.String.format;
@@ -179,7 +178,7 @@ public class SearchAction implements HotspotsWsAction {
     validateParameters(wsRequest);
     try (DbSession dbSession = dbClient.openSession(false)) {
       checkIfNeedIssueSync(dbSession, wsRequest);
-      Optional<ComponentDto> project = getAndValidateProjectOrApplication(dbSession, wsRequest);
+      Optional<ProjectAndBranch> project = getAndValidateProjectOrApplication(dbSession, wsRequest);
       SearchResponseData searchResponseData = searchHotspots(wsRequest, dbSession, project.orElse(null));
       loadComponents(dbSession, searchResponseData);
       loadRules(dbSession, searchResponseData);
@@ -327,24 +326,22 @@ public class SearchAction implements HotspotsWsAction {
     action.setResponseExample(getClass().getResource("search-example.json"));
   }
 
-  private Optional<ComponentDto> getAndValidateProjectOrApplication(DbSession dbSession, WsRequest wsRequest) {
+  private Optional<ProjectAndBranch> getAndValidateProjectOrApplication(DbSession dbSession, WsRequest wsRequest) {
     return wsRequest.getProjectKey().map(projectKey -> {
-      ComponentDto project = getProject(dbSession, projectKey, wsRequest.getBranch().orElse(null), wsRequest.getPullRequest().orElse(null));
-      if (!Scopes.PROJECT.equals(project.scope()) || !SUPPORTED_QUALIFIERS.contains(project.qualifier()) || !project.isEnabled()) {
+      ProjectAndBranch appOrProjectAndBranch = componentFinder.getAppOrProjectAndBranch(dbSession, projectKey, wsRequest.getBranch().orElse(null),
+        wsRequest.getPullRequest().orElse(null));
+
+      if (!SUPPORTED_QUALIFIERS.contains(appOrProjectAndBranch.getProject().getQualifier())) {
         throw new NotFoundException(format("Project '%s' not found", projectKey));
       }
-      userSession.checkComponentPermission(USER, project);
-      userSession.checkChildProjectsPermission(USER, project);
-      return project;
+      userSession.checkProjectPermission(USER, appOrProjectAndBranch.getProject());
+      userSession.checkChildProjectsPermission(USER, appOrProjectAndBranch.getProject());
+      return appOrProjectAndBranch;
     });
   }
 
-  private ComponentDto getProject(DbSession dbSession, String projectKey, @Nullable String branch, @Nullable String pullRequest) {
-    return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest);
-  }
-
-  private SearchResponseData searchHotspots(WsRequest wsRequest, DbSession dbSession, @Nullable ComponentDto project) {
-    SearchResponse result = doIndexSearch(wsRequest, dbSession, project);
+  private SearchResponseData searchHotspots(WsRequest wsRequest, DbSession dbSession, @Nullable ProjectAndBranch projectorApp) {
+    SearchResponse result = doIndexSearch(wsRequest, dbSession, projectorApp);
     List<String> issueKeys = Arrays.stream(result.getHits().getHits())
       .map(SearchHit::getId)
       .collect(toList(result.getHits().getHits().length));
@@ -371,28 +368,29 @@ public class SearchAction implements HotspotsWsAction {
       .toList();
   }
 
-  private SearchResponse doIndexSearch(WsRequest wsRequest, DbSession dbSession, @Nullable ComponentDto project) {
+  private SearchResponse doIndexSearch(WsRequest wsRequest, DbSession dbSession, @Nullable ProjectAndBranch projectOrAppAndBranch) {
     var builder = IssueQuery.builder()
       .types(singleton(RuleType.SECURITY_HOTSPOT.name()))
       .sort(IssueQuery.SORT_HOTSPOTS)
       .asc(true)
       .statuses(wsRequest.getStatus().map(Collections::singletonList).orElse(STATUSES));
 
-    if (project != null) {
-      String projectUuid = firstNonNull(project.getMainBranchProjectUuid(), project.uuid());
-      if (Qualifiers.APP.equals(project.qualifier())) {
-        builder.viewUuids(singletonList(projectUuid));
+    if (projectOrAppAndBranch != null) {
+      ProjectDto projectOrApp = projectOrAppAndBranch.getProject();
+
+      if (Qualifiers.APP.equals(projectOrApp.getQualifier())) {
+        builder.viewUuids(singletonList(projectOrApp.getUuid()));
         if (wsRequest.isInNewCodePeriod() && wsRequest.getPullRequest().isEmpty()) {
-          addInNewCodePeriodFilterByProjects(builder, dbSession, project);
+          addInNewCodePeriodFilterByProjects(builder, dbSession, projectOrAppAndBranch.getBranch());
         }
       } else {
-        builder.projectUuids(singletonList(projectUuid));
+        builder.projectUuids(singletonList(projectOrApp.getUuid()));
         if (wsRequest.isInNewCodePeriod() && wsRequest.getPullRequest().isEmpty()) {
-          addInNewCodePeriodFilter(dbSession, project, builder);
+          addInNewCodePeriodFilter(dbSession, projectOrApp, builder);
         }
       }
 
-      addMainBranchFilter(project, builder);
+      addMainBranchFilter(projectOrAppAndBranch.getBranch(), builder);
     }
 
     if (!wsRequest.getHotspotKeys().isEmpty()) {
@@ -458,17 +456,17 @@ public class SearchAction implements HotspotsWsAction {
     }
   }
 
-  private static void addMainBranchFilter(@NotNull ComponentDto project, IssueQuery.Builder builder) {
-    if (project.getMainBranchProjectUuid() == null) {
+  private static void addMainBranchFilter(@NotNull BranchDto branch, IssueQuery.Builder builder) {
+    if (branch.isMain()) {
       builder.mainBranch(true);
     } else {
-      builder.branchUuid(project.uuid());
+      builder.branchUuid(branch.getUuid());
       builder.mainBranch(false);
     }
   }
 
-  private void addInNewCodePeriodFilter(DbSession dbSession, @NotNull ComponentDto project, IssueQuery.Builder builder) {
-    Optional<SnapshotDto> snapshot = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, project.uuid());
+  private void addInNewCodePeriodFilter(DbSession dbSession, @NotNull ProjectDto project, IssueQuery.Builder builder) {
+    Optional<SnapshotDto> snapshot = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, project.getUuid());
 
     boolean isLastAnalysisUsingReferenceBranch = snapshot.map(SnapshotDto::getPeriodMode)
       .orElse("").equals(REFERENCE_BRANCH.name());
@@ -484,21 +482,23 @@ public class SearchAction implements HotspotsWsAction {
     }
   }
 
-  private void addInNewCodePeriodFilterByProjects(IssueQuery.Builder builder, DbSession dbSession, ComponentDto application) {
-    Set<String> projectUuids;
-    if (application.getMainBranchProjectUuid() == null) {
-      projectUuids = dbClient.applicationProjectsDao().selectProjects(dbSession, application.uuid()).stream()
+  private void addInNewCodePeriodFilterByProjects(IssueQuery.Builder builder, DbSession dbSession, BranchDto appBranch) {
+    Set<String> branchUuids;
+
+    if (appBranch.isMain()) {
+      // TODO assuming project uuid matches main branch uuid
+      branchUuids = dbClient.applicationProjectsDao().selectProjects(dbSession, appBranch.getProjectUuid()).stream()
         .map(ProjectDto::getUuid)
         .collect(Collectors.toSet());
     } else {
-      projectUuids = dbClient.applicationProjectsDao().selectProjectBranchesFromAppBranchUuid(dbSession, application.uuid()).stream()
+      branchUuids = dbClient.applicationProjectsDao().selectProjectBranchesFromAppBranchUuid(dbSession, appBranch.getUuid()).stream()
         .map(BranchDto::getUuid)
         .collect(Collectors.toSet());
     }
 
     long now = system2.now();
 
-    List<SnapshotDto> snapshots = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids);
+    List<SnapshotDto> snapshots = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, branchUuids);
 
     Set<String> newCodeReferenceByProjects = snapshots
       .stream()
index d284e60e318825b8048ea4d74d4858c09d56ae57..96c6a7a3968df1e2e8ab7c0e5a9b6ba0bd70facd 100644 (file)
@@ -97,8 +97,8 @@ public class LiveMeasureComputerImpl implements LiveMeasureComputer {
     }
 
     BranchDto branch = loadBranch(dbSession, branchComponent);
-    Configuration config = projectConfigurationLoader.loadProjectConfiguration(dbSession, branch);
     ProjectDto project = loadProject(dbSession, branch.getProjectUuid());
+    Configuration config = projectConfigurationLoader.loadBranchConfiguration(dbSession, branch);
     QualityGate qualityGate = qGateComputer.loadQualityGate(dbSession, project, branch);
     MeasureMatrix matrix = loadMeasureMatrix(dbSession, components.getAllUuids(), qualityGate);
 
index c4129a81e7fb10d15962779bc5b2431fe06636a9..8f325fd65539b335298e1b8b5694acf0a9cd0b3f 100644 (file)
@@ -165,7 +165,7 @@ public class ComponentAction implements MeasuresWsAction {
 
       Optional<Measures.Period> period = snapshotToWsPeriods(analysis);
       Optional<RefComponent> reference = getReference(dbSession, component);
-      return buildResponse(request, component, reference, measuresByMetric, metrics, period);
+      return buildResponse(dbSession, request, component, reference, measuresByMetric, metrics, period);
     }
   }
 
@@ -246,10 +246,9 @@ public class ComponentAction implements MeasuresWsAction {
     return refBranch.map(rb -> new RefComponent(rb, refComponent.get()));
   }
 
-  private static ComponentWsResponse buildResponse(ComponentRequest request, ComponentDto component, Optional<RefComponent> reference,
+  private ComponentWsResponse buildResponse(DbSession dbSession, ComponentRequest request, ComponentDto component, Optional<RefComponent> reference,
     Map<MetricDto, LiveMeasureDto> measuresByMetric, Collection<MetricDto> metrics, Optional<Measures.Period> period) {
     ComponentWsResponse.Builder response = ComponentWsResponse.newBuilder();
-    boolean isMainBranch = component.getMainBranchProjectUuid() == null;
 
     if (reference.isPresent()) {
       BranchDto refBranch = reference.get().getRefBranch();
@@ -257,6 +256,7 @@ public class ComponentAction implements MeasuresWsAction {
       response.setComponent(componentDtoToWsComponent(component, measuresByMetric, singletonMap(refComponent.uuid(), refComponent),
         refBranch.isMain() ? null : refBranch.getBranchKey(), null));
     } else {
+      boolean isMainBranch = dbClient.branchDao().selectByUuid(dbSession, component.branchUuid()).map(BranchDto::isMain).orElse(true);
       response.setComponent(componentDtoToWsComponent(component, measuresByMetric, emptyMap(), isMainBranch ? null : request.getBranch(), request.getPullRequest()));
     }
 
index da343f45723c7c3ed581a63c9995fe3f94a9d297..35dfde3e2f16865393144ac071a26a0b7a11519d 100644 (file)
@@ -270,7 +270,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
   private ComponentTreeWsResponse doHandle(ComponentTreeRequest request) {
     ComponentTreeData data = load(request);
     if (data.getComponents() == null) {
-      return emptyResponse(data.getBaseComponent(), request);
+      return emptyResponse(data.getBaseComponent(), data.getBranch(), request);
     }
 
     return buildResponse(
@@ -290,7 +290,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
       .setTotal(paging.total())
       .build();
 
-    boolean isMainBranch = data.getBaseComponent().getMainBranchProjectUuid() == null;
+    boolean isMainBranch = data.getBranch() == null || data.getBranch().isMain();
     response.setBaseComponent(
       toWsComponent(
         data.getBaseComponent(),
@@ -333,14 +333,14 @@ public class ComponentTreeAction implements MeasuresWsAction {
     return additionalFields != null && additionalFields.contains(ADDITIONAL_METRICS);
   }
 
-  private static ComponentTreeWsResponse emptyResponse(@Nullable ComponentDto baseComponent, ComponentTreeRequest request) {
+  private static ComponentTreeWsResponse emptyResponse(@Nullable ComponentDto baseComponent, @Nullable BranchDto branch, ComponentTreeRequest request) {
     ComponentTreeWsResponse.Builder response = ComponentTreeWsResponse.newBuilder();
     response.getPagingBuilder()
       .setPageIndex(request.getPage())
       .setPageSize(request.getPageSize())
       .setTotal(0);
     if (baseComponent != null) {
-      boolean isMainBranch = baseComponent.getMainBranchProjectUuid() == null;
+      boolean isMainBranch = branch == null || branch.isMain();
       response.setBaseComponent(componentDtoToWsComponent(baseComponent, isMainBranch ? null : request.getBranch(), request.getPullRequest()));
     }
     return response.build();
@@ -415,9 +415,13 @@ public class ComponentTreeAction implements MeasuresWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       ComponentDto baseComponent = loadComponent(dbSession, wsRequest);
       checkPermissions(baseComponent);
+      // portfolios don't have branches
+      BranchDto branchDto = dbClient.branchDao().selectByUuid(dbSession, baseComponent.branchUuid()).orElse(null);
+
       Optional<SnapshotDto> baseSnapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, baseComponent.branchUuid());
       if (baseSnapshot.isEmpty()) {
         return ComponentTreeData.builder()
+          .setBranch(branchDto)
           .setBaseComponent(baseComponent)
           .build();
       }
@@ -455,6 +459,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
 
       return ComponentTreeData.builder()
         .setBaseComponent(baseComponent)
+        .setBranch(branchDto)
         .setComponentsFromDb(components)
         .setComponentCount(componentCount)
         .setBranchByReferenceUuid(branchByReferenceUuid)
index 4c86e9f666884482f1ac4d75739fffb6fc19230f..7ddd4bafb9f16a6573142cb22a80f53448c8c8f9 100644 (file)
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.measure.LiveMeasureDto;
 import org.sonar.db.metric.MetricDto;
@@ -35,6 +36,7 @@ import static java.util.Objects.requireNonNull;
 
 class ComponentTreeData {
   private final ComponentDto baseComponent;
+  private final BranchDto branch;
   private final List<ComponentDto> components;
   private final int componentCount;
   private final Map<String, ComponentDto> referenceComponentsByUuid;
@@ -47,6 +49,7 @@ class ComponentTreeData {
     this.baseComponent = builder.baseComponent;
     this.components = builder.componentsFromDb;
     this.componentCount = builder.componentCount;
+    this.branch = builder.branch;
     this.referenceComponentsByUuid = builder.referenceComponentsByUuid;
     this.branchByReferenceUuid = builder.branchByReferenceUuid;
     this.metrics = builder.metrics;
@@ -62,6 +65,11 @@ class ComponentTreeData {
     return baseComponent;
   }
 
+  @CheckForNull
+  public BranchDto getBranch() {
+    return branch;
+  }
+
   @CheckForNull
   List<ComponentDto> getComponents() {
     return components;
@@ -102,6 +110,8 @@ class ComponentTreeData {
     private int componentCount;
     private List<MetricDto> metrics;
     private Measures.Period period;
+    private BranchDto branch;
+
     private Table<String, MetricDto, Measure> measuresByComponentUuidAndMetric;
 
     private Builder() {
@@ -128,6 +138,11 @@ class ComponentTreeData {
       return this;
     }
 
+    public Builder setBranch(@Nullable BranchDto branch) {
+      this.branch = branch;
+      return this;
+    }
+
     public Builder setMetrics(List<MetricDto> metrics) {
       this.metrics = metrics;
       return this;
index e431283145c8420e8561c0864f2634a37dfc5550..a38add5920c6e07a399334e03885c636187df779 100644 (file)
@@ -94,7 +94,7 @@ public class BulkDeleteAction implements ProjectsWsAction {
       .setChangelog(
         new Change("7.8", parameterRequiredMessage),
         new Change("9.1", "The parameter '" + PARAM_ANALYZED_BEFORE + "' "
-        + "takes into account the analysis of all branches and pull requests, not only the main branch."));
+          + "takes into account the analysis of all branches and pull requests, not only the main branch."));
 
     action
       .createParam(PARAM_PROJECTS)
@@ -115,7 +115,7 @@ public class BulkDeleteAction implements ProjectsWsAction {
 
     action.createParam(PARAM_VISIBILITY)
       .setDescription("Filter the projects that should be visible to everyone (%s), or only specific user/groups (%s).<br/>" +
-        "If no visibility is specified, the default project visibility will be used.",
+          "If no visibility is specified, the default project visibility will be used.",
         Visibility.PUBLIC.getLabel(), Visibility.PRIVATE.getLabel())
       .setRequired(false)
       .setInternal(true)
@@ -148,7 +148,7 @@ public class BulkDeleteAction implements ProjectsWsAction {
       Set<ComponentDto> componentDtos = new HashSet<>(dbClient.componentDao().selectByQuery(dbSession, query, 0, Integer.MAX_VALUE));
 
       try {
-        componentDtos.forEach(p -> componentCleanerService.delete(dbSession, p));
+        componentDtos.forEach(p -> componentCleanerService.deleteComponent(dbSession, p));
       } finally {
         projectLifeCycleListeners.onProjectsDeleted(componentDtos.stream().map(Project::from).collect(MoreCollectors.toSet(componentDtos.size())));
       }
index ab2002a6941c202fa232418fe64e4d15378bd5ad..deb01610b6ec8f245490121d9c76908148ec047a 100644 (file)
@@ -25,6 +25,7 @@ import com.google.common.collect.Multimap;
 import com.google.common.collect.Ordering;
 import com.google.common.collect.TreeMultimap;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -124,11 +125,9 @@ public class ValuesAction implements SettingsWsAction {
     try (DbSession dbSession = dbClient.openSession(true)) {
       ValuesRequest valuesRequest = ValuesRequest.from(request);
       Optional<ComponentDto> component = loadComponent(dbSession, valuesRequest);
-      BranchDto branchDto = component.map(c -> componentFinder.getBranchByUuid(dbSession, c.branchUuid())).orElse(null);
-
       Set<String> keys = loadKeys(valuesRequest);
       Map<String, String> keysToDisplayMap = getKeysToDisplayMap(keys);
-      List<Setting> settings = loadSettings(dbSession, component, keysToDisplayMap.keySet(), branchDto);
+      List<Setting> settings = loadSettings(dbSession, component, keysToDisplayMap.keySet());
       return new ValuesResponseBuilder(settings, component, keysToDisplayMap).build();
     }
   }
@@ -160,28 +159,33 @@ public class ValuesAction implements SettingsWsAction {
     return Optional.of(component);
   }
 
-  private List<Setting> loadSettings(DbSession dbSession, Optional<ComponentDto> component, Set<String> keys, @Nullable BranchDto branchDto) {
+  private List<Setting> loadSettings(DbSession dbSession, Optional<ComponentDto> component, Set<String> keys) {
     // List of settings must be kept in the following orders : default -> global -> component -> branch
     List<Setting> settings = new ArrayList<>();
     settings.addAll(loadDefaultValues(keys));
     settings.addAll(loadGlobalSettings(dbSession, keys));
-    String branch = getBranchKeySafely(branchDto);
-    if (component.isPresent() && branch != null && component.get().getMainBranchProjectUuid() != null) {
-      ComponentDto project = componentFinder.getByUuidFromMainBranch(dbSession, component.get().getMainBranchProjectUuid());
-      settings.addAll(loadComponentSettings(dbSession, keys, project).values());
-    }
-    component.ifPresent(componentDto -> settings.addAll(loadComponentSettings(dbSession, keys, componentDto).values()));
+    component.ifPresent(c -> settings.addAll(loadComponentSettings(dbSession, c, keys)));
     return settings.stream()
       .filter(s -> settingsWsSupport.isVisible(s.getKey(), component))
       .toList();
   }
 
-  @CheckForNull
-  private static String getBranchKeySafely(@Nullable BranchDto branchDto) {
-    if (branchDto != null) {
-      return branchDto.isMain() ? null : branchDto.getBranchKey();
+  private Collection<Setting> loadComponentSettings(DbSession dbSession, ComponentDto componentDto, Set<String> keys) {
+    BranchDto branchDto = componentFinder.getBranchByUuid(dbSession, componentDto.branchUuid());
+
+    List<String> componentUuids = new LinkedList<>();
+    if (!branchDto.isMain()) {
+      ComponentDto mainBranchComponent = componentFinder.getByKey(dbSession, componentDto.getKey());
+      if (!mainBranchComponent.isRoot()) {
+        componentUuids.add(mainBranchComponent.branchUuid());
+      }
+      componentUuids.add(mainBranchComponent.uuid());
+    }
+    if (!componentDto.isRoot()) {
+      componentUuids.add(componentDto.branchUuid());
     }
-    return null;
+    componentUuids.add(componentDto.uuid());
+    return loadComponentsSettings(dbSession, keys, componentUuids);
   }
 
   private List<Setting> loadDefaultValues(Set<String> keys) {
@@ -209,14 +213,9 @@ public class ValuesAction implements SettingsWsAction {
   }
 
   /**
-   * Return list of settings by component uuid
+   * Return list of settings by component uuids
    */
-  private Multimap<String, Setting> loadComponentSettings(DbSession dbSession, Set<String> keys, ComponentDto component) {
-    List<String> componentUuids = new LinkedList<>();
-    if (!component.uuid().equals(component.branchUuid())) {
-      componentUuids.add(component.branchUuid());
-    }
-    componentUuids.add(component.uuid());
+  private Collection<Setting> loadComponentsSettings(DbSession dbSession, Set<String> keys, List<String> componentUuids) {
     List<PropertyDto> properties = dbClient.propertiesDao().selectPropertiesByKeysAndComponentUuids(dbSession, keys, componentUuids);
     List<PropertyDto> propertySets = dbClient.propertiesDao().selectPropertiesByKeysAndComponentUuids(dbSession, getPropertySetKeys(properties), componentUuids);
 
@@ -227,7 +226,7 @@ public class ValuesAction implements SettingsWsAction {
       settingsByUuid.put(componentUuid,
         Setting.createFromDto(propertyDto, getPropertySets(propertyKey, propertySets, componentUuid), propertyDefinitions.get(propertyKey)));
     }
-    return settingsByUuid;
+    return settingsByUuid.values();
   }
 
   private Set<String> getPropertySetKeys(List<PropertyDto> properties) {
index be693c44f8e97ab444946772b1429c81af45d77d..d24aec74705acf40bd9f5e225afecda8c5d8a19f 100644 (file)
@@ -158,13 +158,13 @@ public class LinesAction implements SourcesWsAction {
 
   private ComponentDto loadComponent(DbSession dbSession, Request wsRequest) {
     String componentKey = wsRequest.param(PARAM_KEY);
-    String componentId = wsRequest.param(PARAM_UUID);
+    String componentUuid = wsRequest.param(PARAM_UUID);
     String branch = wsRequest.param(PARAM_BRANCH);
     String pullRequest = wsRequest.param(PARAM_PULL_REQUEST);
-    checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'",
+    checkArgument(componentUuid == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'",
       PARAM_UUID, PARAM_BRANCH, PARAM_PULL_REQUEST);
     if (branch == null && pullRequest == null) {
-      return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, UUID_AND_KEY);
+      return componentFinder.getByUuidOrKey(dbSession, componentUuid, componentKey, UUID_AND_KEY);
     }
 
     checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_KEY);
index 8c8de6fcf660042d56a1c52ac1e71234e4487d42..58e63b6f85897852ee2949529a8032156f5e19a9 100644 (file)
@@ -26,7 +26,6 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Predicate;
-import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.resources.Qualifiers;
@@ -43,6 +42,7 @@ import org.sonar.api.web.UserRole;
 import org.sonar.api.web.page.Page;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.measure.LiveMeasureDto;
@@ -149,9 +149,10 @@ public class ComponentAction implements NavigationWsAction {
       String pullRequest = request.param(PARAM_PULL_REQUEST);
       ComponentDto component = componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, componentKey, branch, pullRequest);
       checkComponentNotAModuleAndNotADirectory(component);
-      ComponentDto rootProjectOrBranch = getRootProjectOrBranch(component, session);
-      ComponentDto rootProject = rootProjectOrBranch.getMainBranchProjectUuid() == null ? rootProjectOrBranch
-        : componentFinder.getByUuidFromMainBranch(session, rootProjectOrBranch.getMainBranchProjectUuid());
+      ComponentDto rootComponent = getRootProjectOrBranch(component, session);
+      // will be empty for portfolios
+      Optional<BranchDto> branchDto = dbClient.branchDao().selectByUuid(session, rootComponent.branchUuid());
+      String projectOrPortfolioUuid = branchDto.map(BranchDto::getProjectUuid).orElse(rootComponent.branchUuid());
       if (!userSession.hasComponentPermission(USER, component) &&
         !userSession.hasComponentPermission(ADMIN, component) &&
         !userSession.isSystemAdministrator()) {
@@ -161,11 +162,10 @@ public class ComponentAction implements NavigationWsAction {
 
       try (JsonWriter json = response.newJsonWriter()) {
         json.beginObject();
-        boolean isFavourite = isFavourite(session, rootProject, component);
-        String branchKey = getBranchKey(session, component);
-        writeComponent(json, component, analysis.orElse(null), isFavourite, branchKey);
+        boolean isFavourite = isFavourite(session, projectOrPortfolioUuid, component);
+        writeComponent(json, component, analysis.orElse(null), isFavourite, branchDto.map(BranchDto::getBranchKey).orElse(null));
         writeProfiles(json, session, component);
-        writeQualityGate(json, session, rootProject);
+        writeQualityGate(json, session, projectOrPortfolioUuid);
         if (userSession.hasComponentPermission(ADMIN, component) ||
           userSession.hasPermission(ADMINISTER_QUALITY_PROFILES) ||
           userSession.hasPermission(ADMINISTER_QUALITY_GATES)) {
@@ -177,14 +177,6 @@ public class ComponentAction implements NavigationWsAction {
     }
   }
 
-  @CheckForNull
-  private String getBranchKey(DbSession session, ComponentDto component) {
-    // TODO portfolios may have no branch, so we shouldn't faul?
-    return dbClient.branchDao().selectByUuid(session, component.branchUuid())
-      .map(b -> b.isMain() ? null : b.getBranchKey())
-      .orElse(null);
-  }
-
   private static void checkComponentNotAModuleAndNotADirectory(ComponentDto component) {
     BadRequestException.checkRequest(!MODULE_OR_DIR_QUALIFIERS.contains(component.qualifier()), "Operation not supported for module or directory components");
   }
@@ -239,11 +231,11 @@ public class ComponentAction implements NavigationWsAction {
     }
   }
 
-  private boolean isFavourite(DbSession session, ComponentDto rootComponent, ComponentDto component) {
+  private boolean isFavourite(DbSession session, String projectOrPortfolioUuid, ComponentDto component) {
     PropertyQuery propertyQuery = PropertyQuery.builder()
       .setUserUuid(userSession.getUuid())
       .setKey("favourite")
-      .setComponentUuid(isSubview(component) ? component.uuid() : rootComponent.uuid())
+      .setComponentUuid(isSubview(component) ? component.uuid() : projectOrPortfolioUuid)
       .build();
     List<PropertyDto> componentFavourites = dbClient.propertiesDao().selectByQuery(propertyQuery, session);
     return componentFavourites.size() == 1;
@@ -266,8 +258,8 @@ public class ComponentAction implements NavigationWsAction {
     json.endArray();
   }
 
-  private void writeQualityGate(JsonWriter json, DbSession session, ComponentDto component) {
-    var qualityGateData = qualityGateFinder.getEffectiveQualityGate(session, component.uuid());
+  private void writeQualityGate(JsonWriter json, DbSession session, String projectOrPortfolioUuid) {
+    var qualityGateData = qualityGateFinder.getEffectiveQualityGate(session, projectOrPortfolioUuid);
     json.name("qualityGate").beginObject()
       .prop("key", qualityGateData.getUuid())
       .prop("name", qualityGateData.getName())
index 36813971f4f0b738b967acddd411dc54ae876472..4984ab3dd8d188df278169b2204ff534a9b5c60a 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.setting;
 import org.sonar.api.config.Configuration;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.ComponentDto;
 
 public class TestProjectConfigurationLoader implements ProjectConfigurationLoader {
 
@@ -33,7 +32,12 @@ public class TestProjectConfigurationLoader implements ProjectConfigurationLoade
   }
 
   @Override
-  public Configuration loadProjectConfiguration(DbSession dbSession, BranchDto branch) {
+  public Configuration loadBranchConfiguration(DbSession dbSession, BranchDto branch) {
+    return config;
+  }
+
+  @Override
+  public Configuration loadProjectConfiguration(DbSession dbSession, String projectUuid) {
     return config;
   }
 }