]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13399 return 'needIssueSync' flag in api/components/show WS
authorJacek <jacek.poreda@sonarsource.com>
Tue, 2 Jun 2020 09:33:40 +0000 (11:33 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 26 Jun 2020 20:04:56 +0000 (20:04 +0000)
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/ShowAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ws/ShowActionTest.java

index aec3bfa028462fb2c9975e723d030fd22b5112d3..04d03bb1557d7ad378e00bc59bb80d8488ccb14f 100644 (file)
@@ -119,6 +119,10 @@ public class BranchDao implements Dao {
     return executeLargeInputs(uuids, mapper(session)::selectProjectUuidsWithIssuesNeedSync);
   }
 
+  public boolean hasAnyBranchWhereNeedIssueSync(DbSession session, boolean needIssueSync) {
+    return mapper(session).hasAnyBranchWhereNeedIssueSync(needIssueSync) > 0;
+  }
+
   public boolean hasNonMainBranches(DbSession dbSession) {
     return mapper(dbSession).countNonMainBranches() > 0L;
   }
index 689c1bc5bf690057d8bff2e3b0d936575416032b..53c29667441fea0c4414655f1d1134db9da50ce4 100644 (file)
@@ -55,4 +55,5 @@ public interface BranchMapper {
 
   long countByTypeAndCreationDate(@Param("branchType") String branchType, @Param("sinceDate") long sinceDate);
 
+  short hasAnyBranchWhereNeedIssueSync(@Param("needIssueSync") boolean needIssueSync);
 }
index c2e0e21a0a48d5187cb91277ffcec220b7a6a167..dbd6b6dceb54ce87ded5ab5c96d05be68473790d 100644 (file)
     select count(pb.uuid)
     from project_branches pb
     where
-      pb.branch_type = #{branchType, jdbcType=VARCHAR}
-      and pb.created_at &gt;= #{sinceDate, jdbcType=BIGINT}
+    pb.branch_type = #{branchType, jdbcType=VARCHAR}
+    and pb.created_at &gt;= #{sinceDate, jdbcType=BIGINT}
+  </select>
+
+  <select id="hasAnyBranchWhereNeedIssueSync" parameterType="map" resultType="short">
+    select
+    case when exists
+    (
+    select pb.uuid from project_branches pb where need_issue_sync = #{needIssueSync, jdbcType=BOOLEAN}
+    )
+    then 1
+    else 0
+    end
   </select>
 
 </mapper>
index 7cdb7809998ae214ebb7877ac7f4e18e2facc196..96edb3ba470ba4feab0ebf653f767950c2cab6fb 100644 (file)
@@ -538,6 +538,22 @@ public class BranchDaoTest {
     assertThat(underTest.hasNonMainBranches(dbSession)).isTrue();
   }
 
+  @Test
+  public void hasAnyBranchWhereNeedIssueSync() {
+    assertThat(underTest.hasAnyBranchWhereNeedIssueSync(dbSession, true)).isFalse();
+    assertThat(underTest.hasAnyBranchWhereNeedIssueSync(dbSession, false)).isFalse();
+
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setNeedIssueSync(false));
+
+    assertThat(underTest.hasAnyBranchWhereNeedIssueSync(dbSession, true)).isFalse();
+    assertThat(underTest.hasAnyBranchWhereNeedIssueSync(dbSession, false)).isTrue();
+
+    project = db.components().insertPrivateProject();
+    branch = db.components().insertProjectBranch(project, b -> b.setNeedIssueSync(true));
+    assertThat(underTest.hasAnyBranchWhereNeedIssueSync(dbSession, true)).isTrue();
+  }
+
   @Test
   public void countByTypeAndCreationDate() {
     assertThat(underTest.countByTypeAndCreationDate(dbSession, BranchType.BRANCH, 0L)).isEqualTo(0);
index e6812eef9a1643ebbc01ba3e13023b3c5bf78908..2dd0bf2a036e5c5460c51165010cc7c59967d96a 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.component.ws;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -56,6 +57,7 @@ import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COM
 
 public class ShowAction implements ComponentsWsAction {
   private static final Set<String> PROJECT_OR_APP_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.APP);
+  private static final Set<String> VIEW_OR_SUBVIEW_QUALIFIERS = ImmutableSet.of(Qualifiers.VIEW, Qualifiers.SUBVIEW);
   private final UserSession userSession;
   private final DbClient dbClient;
   private final ComponentFinder componentFinder;
@@ -109,7 +111,7 @@ public class ShowAction implements ComponentsWsAction {
       Optional<SnapshotDto> lastAnalysis = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, component.projectUuid());
       List<ComponentDto> ancestors = dbClient.componentDao().selectAncestors(dbSession, component);
       OrganizationDto organizationDto = componentFinder.getOrganization(dbSession, component);
-      return buildResponse(dbClient, dbSession, component, organizationDto, ancestors, lastAnalysis.orElse(null));
+      return buildResponse(dbSession, component, organizationDto, ancestors, lastAnalysis.orElse(null));
     }
   }
 
@@ -121,34 +123,38 @@ public class ShowAction implements ComponentsWsAction {
     return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
   }
 
-  private static ShowWsResponse buildResponse(DbClient dbClient, DbSession dbSession, ComponentDto component,
+  private ShowWsResponse buildResponse(DbSession dbSession, ComponentDto component,
     OrganizationDto organizationDto, List<ComponentDto> orderedAncestors,
     @Nullable SnapshotDto lastAnalysis) {
     ShowWsResponse.Builder response = ShowWsResponse.newBuilder();
-    response.setComponent(toWsComponent(dbClient, dbSession, component, organizationDto, lastAnalysis));
-    addAncestorsToResponse(dbClient, dbSession, response, orderedAncestors, organizationDto, lastAnalysis);
+    response.setComponent(toWsComponent(dbSession, component, organizationDto, lastAnalysis));
+    addAncestorsToResponse(dbSession, response, orderedAncestors, organizationDto, lastAnalysis);
     return response.build();
   }
 
-  private static void addAncestorsToResponse(DbClient dbClient, DbSession dbSession, ShowWsResponse.Builder response, List<ComponentDto> orderedAncestors,
+  private void addAncestorsToResponse(DbSession dbSession, ShowWsResponse.Builder response, List<ComponentDto> orderedAncestors,
     OrganizationDto organizationDto,
     @Nullable SnapshotDto lastAnalysis) {
     // ancestors are ordered from root to leaf, whereas it's the opposite in WS response
     int size = orderedAncestors.size() - 1;
     IntStream.rangeClosed(0, size).forEach(
-      index -> response.addAncestors(toWsComponent(dbClient, dbSession, orderedAncestors.get(size - index), organizationDto, lastAnalysis)));
+      index -> response.addAncestors(toWsComponent(dbSession, orderedAncestors.get(size - index), organizationDto, lastAnalysis)));
   }
 
-  private static Components.Component.Builder toWsComponent(DbClient dbClient, DbSession dbSession, ComponentDto component, OrganizationDto organizationDto,
+  private Components.Component.Builder toWsComponent(DbSession dbSession, ComponentDto component, OrganizationDto organizationDto,
     @Nullable SnapshotDto lastAnalysis) {
     if (isProjectOrApp(component)) {
       ProjectDto project = dbClient.projectDao().selectProjectOrAppByKey(dbSession, component.getKey())
         .orElseThrow(() -> new IllegalStateException("Project is in invalid state."));
-      return projectOrAppToWsComponent(project, organizationDto, lastAnalysis);
+      boolean needIssueSync = needIssueSync(dbSession, component, project);
+      return projectOrAppToWsComponent(project, organizationDto, lastAnalysis)
+        .setNeedIssueSync(needIssueSync);
     } else {
       Optional<ProjectDto> parentProject = dbClient.projectDao().selectByUuid(dbSession,
         ofNullable(component.getMainBranchProjectUuid()).orElse(component.projectUuid()));
-      return componentDtoToWsComponent(component, parentProject.orElse(null), organizationDto, lastAnalysis);
+      boolean needIssueSync = needIssueSync(dbSession, component, parentProject.orElse(null));
+      return componentDtoToWsComponent(component, parentProject.orElse(null), organizationDto, lastAnalysis)
+        .setNeedIssueSync(needIssueSync);
     }
   }
 
@@ -156,6 +162,14 @@ public class ShowAction implements ComponentsWsAction {
     return component.getMainBranchProjectUuid() == null && PROJECT_OR_APP_QUALIFIERS.contains(component.qualifier());
   }
 
+  private boolean needIssueSync(DbSession dbSession, ComponentDto component, @Nullable ProjectDto projectDto) {
+    if (projectDto == null || VIEW_OR_SUBVIEW_QUALIFIERS.contains(component.qualifier())) {
+      return dbClient.branchDao().hasAnyBranchWhereNeedIssueSync(dbSession, true);
+    }
+
+    return !dbClient.branchDao().selectProjectUuidsWithIssuesNeedSync(dbSession, Sets.newHashSet(projectDto.getUuid())).isEmpty();
+  }
+
   private static Request toShowWsRequest(org.sonar.api.server.ws.Request request) {
     return new Request()
       .setComponentKey(request.mandatoryParam(PARAM_COMPONENT))
index 39f4754f7e5c454fad1e8a34c75bd0307c1a6393..6d0252f1a5f4e9a2f21502c232f93f5481a15b0a 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.component.ws;
 
 import java.util.Date;
+import java.util.Optional;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
@@ -30,6 +31,7 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.component.TestComponentFinder;
@@ -46,6 +48,7 @@ import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
 import static org.sonar.api.utils.DateUtils.parseDateTime;
 import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.BranchType.BRANCH;
 import static org.sonar.db.component.BranchType.PULL_REQUEST;
 import static org.sonar.db.component.ComponentTesting.newDirectory;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
@@ -310,6 +313,72 @@ public class ShowActionTest {
         tuple(branch.getKey(), pullRequest, "1.1"));
   }
 
+  @Test
+  public void verify_need_issue_sync_pr() {
+    ComponentDto portfolio1 = db.components().insertPublicPortfolio(db.getDefaultOrganization());
+    ComponentDto portfolio2 = db.components().insertPublicPortfolio(db.getDefaultOrganization());
+    ComponentDto subview = db.components().insertSubView(portfolio1);
+
+    ComponentDto project1 = db.components().insertPrivateProject();
+    ComponentDto branch1 = db.components().insertProjectBranch(project1, b -> b.setBranchType(PULL_REQUEST).setNeedIssueSync(true));
+    ComponentDto module = db.components().insertComponent(newModuleDto(branch1));
+    ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir"));
+    ComponentDto file = db.components().insertComponent(newFileDto(directory));
+
+    ComponentDto project2 = db.components().insertPrivateProject();
+    ComponentDto branch2 = db.components().insertProjectBranch(project2, b -> b.setBranchType(BRANCH).setNeedIssueSync(true));
+    ComponentDto branch3 = db.components().insertProjectBranch(project2, b -> b.setBranchType(BRANCH).setNeedIssueSync(false));
+
+    ComponentDto project3 = db.components().insertPrivateProject();
+    ComponentDto branch4 = db.components().insertProjectBranch(project3, b -> b.setBranchType(PULL_REQUEST).setNeedIssueSync(false));
+    ComponentDto moduleOfBranch4 = db.components().insertComponent(newModuleDto(branch4));
+    ComponentDto directoryOfBranch4 = db.components().insertComponent(newDirectory(moduleOfBranch4, "dir"));
+    ComponentDto fileOfBranch4 = db.components().insertComponent(newFileDto(directoryOfBranch4));
+    ComponentDto branch5 = db.components().insertProjectBranch(project3, b -> b.setBranchType(BRANCH).setNeedIssueSync(false));
+
+    userSession.addMembership(db.getDefaultOrganization());
+    userSession.addProjectPermission(UserRole.USER, project1, project2, project3);
+    userSession.registerComponents(portfolio1, portfolio2, subview, project1, project2, project3);
+
+    //for portfolios, sub-views need issue sync flag is set to true if any project need sync
+    assertNeedIssueSyncEqual(null, null, portfolio1, true);
+    assertNeedIssueSyncEqual(null, null, subview, true);
+    assertNeedIssueSyncEqual(null, null, portfolio2, true);
+
+    //if branch need sync it is propagated to other components
+    assertNeedIssueSyncEqual(null, null, project1, true);
+    assertNeedIssueSyncEqual(branch1.getPullRequest(), null, branch1, true);
+    assertNeedIssueSyncEqual(branch1.getPullRequest(), null, module, true);
+    assertNeedIssueSyncEqual(branch1.getPullRequest(), null, directory, true);
+    assertNeedIssueSyncEqual(branch1.getPullRequest(), null, file, true);
+
+    assertNeedIssueSyncEqual(null, null, project2, true);
+    assertNeedIssueSyncEqual(null, branch2.getBranch(), branch2, true);
+    assertNeedIssueSyncEqual(null, branch3.getBranch(), branch3, true);
+
+    //if all branches are synced, need issue sync on project is is set to false
+    assertNeedIssueSyncEqual(null, null, project3, false);
+    assertNeedIssueSyncEqual(branch4.getPullRequest(), null, branch4, false);
+    assertNeedIssueSyncEqual(branch4.getPullRequest(), null, moduleOfBranch4, false);
+    assertNeedIssueSyncEqual(branch4.getPullRequest(), null, directoryOfBranch4, false);
+    assertNeedIssueSyncEqual(branch4.getPullRequest(), null, fileOfBranch4, false);
+    assertNeedIssueSyncEqual(null, branch5.getBranch(), branch5, false);
+  }
+
+  private void assertNeedIssueSyncEqual(@Nullable String pullRequest, @Nullable String branch, ComponentDto component, boolean needIssueSync) {
+    TestRequest testRequest = ws.newRequest()
+      .setParam(PARAM_COMPONENT, component.getKey());
+
+    Optional.ofNullable(pullRequest).ifPresent(pr -> testRequest.setParam(PARAM_PULL_REQUEST, pr));
+    Optional.ofNullable(branch).ifPresent(br -> testRequest.setParam(PARAM_BRANCH, br));
+
+    ShowWsResponse response = testRequest.executeProtobuf(ShowWsResponse.class);
+
+    assertThat(response.getComponent())
+      .extracting(Component::getNeedIssueSync)
+      .isEqualTo(needIssueSync);
+  }
+
   @Test
   public void throw_ForbiddenException_if_user_doesnt_have_browse_permission_on_project() {
     userSession.logIn();