Browse Source

SONAR-19728 Update ws indexation_status to return project count instead of branch percentage

tags/10.2.0.77647
Eric Giffon 10 months ago
parent
commit
21fb2157a4

+ 0
- 36
server/sonar-db-dao/src/it/java/org/sonar/db/component/BranchDaoIT.java View File

assertThat(underTest.countByTypeAndCreationDate(dbSession, BranchType.PULL_REQUEST, NOW + 100)).isZero(); assertThat(underTest.countByTypeAndCreationDate(dbSession, BranchType.PULL_REQUEST, NOW + 100)).isZero();
} }


@Test
public void countByNeedIssueSync() {
assertThat(underTest.countByNeedIssueSync(dbSession, true)).isZero();
assertThat(underTest.countByNeedIssueSync(dbSession, false)).isZero();

// master branch with flag set to false
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
// branches & PRs
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(true));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(true));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(false));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(false));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(true));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(false));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(true));

assertThat(underTest.countByNeedIssueSync(dbSession, true)).isEqualTo(4);
assertThat(underTest.countByNeedIssueSync(dbSession, false)).isEqualTo(4);
}

@Test
public void countAll() {
assertThat(underTest.countAll(dbSession)).isZero();

ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(true));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(true));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(false));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(false));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(true));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(false));
db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(true));

assertThat(underTest.countAll(dbSession)).isEqualTo(8);
}

@Test @Test
public void selectBranchNeedingIssueSync() { public void selectBranchNeedingIssueSync() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();

+ 28
- 0
server/sonar-db-dao/src/it/java/org/sonar/db/project/ProjectDaoIT.java View File

import org.sonar.db.audit.AuditPersister; import org.sonar.db.audit.AuditPersister;
import org.sonar.db.audit.NoOpAuditPersister; import org.sonar.db.audit.NoOpAuditPersister;
import org.sonar.db.component.BranchDto; import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ProjectData; import org.sonar.db.component.ProjectData;
import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.measure.LiveMeasureDto;
assertThat(projectDtos).extracting(ProjectDto::getName).containsOnly("Project_2", "Project_3"); assertThat(projectDtos).extracting(ProjectDto::getName).containsOnly("Project_2", "Project_3");
} }


@Test
public void countIndexedProjects() {
assertThat(projectDao.countIndexedProjects(db.getSession())).isZero();

// master branch with flag set to false
ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
// branches & PRs
db.components().insertProjectBranch(project1, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(true));
db.components().insertProjectBranch(project1, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(false));
db.components().insertProjectBranch(project2, b -> b.setBranchType(BranchType.BRANCH).setNeedIssueSync(false));
db.components().insertProjectBranch(project1, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(true));
db.components().insertProjectBranch(project1, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(false));
db.components().insertProjectBranch(project2, b -> b.setBranchType(BranchType.PULL_REQUEST).setNeedIssueSync(false));

assertThat(projectDao.countIndexedProjects(db.getSession())).isEqualTo(1);
}

@Test
public void countProjects() {
assertThat(projectDao.countProjects(db.getSession())).isZero();

IntStream.range(0, 10).forEach(x -> db.components().insertPrivateProject());

assertThat(projectDao.countProjects(db.getSession())).isEqualTo(10);
}

private void insertDefaultQualityProfile(String language) { private void insertDefaultQualityProfile(String language) {
QProfileDto profile = db.qualityProfiles().insert(qp -> qp.setIsBuiltIn(true).setLanguage(language)); QProfileDto profile = db.qualityProfiles().insert(qp -> qp.setIsBuiltIn(true).setLanguage(language));
db.qualityProfiles().setAsDefault(profile); db.qualityProfiles().setAsDefault(profile);

+ 0
- 8
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java View File

return mapper(dbSession).countByTypeAndCreationDate(branchType.name(), sinceDate); return mapper(dbSession).countByTypeAndCreationDate(branchType.name(), sinceDate);
} }


public int countByNeedIssueSync(DbSession session, boolean needIssueSync) {
return mapper(session).countByNeedIssueSync(needIssueSync);
}

public int countAll(DbSession session) {
return mapper(session).countAll();
}

private static BranchMapper mapper(DbSession dbSession) { private static BranchMapper mapper(DbSession dbSession) {
return dbSession.getMapper(BranchMapper.class); return dbSession.getMapper(BranchMapper.class);
} }

+ 0
- 4
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java View File



short hasAnyBranchWhereNeedIssueSync(@Param("needIssueSync") boolean needIssueSync); short hasAnyBranchWhereNeedIssueSync(@Param("needIssueSync") boolean needIssueSync);


int countByNeedIssueSync(@Param("needIssueSync") boolean needIssueSync);

int countAll();

List<BranchDto> selectBranchNeedingIssueSync(); List<BranchDto> selectBranchNeedingIssueSync();


List<BranchDto> selectBranchNeedingIssueSyncForProject(@Param("projectUuid") String projectUuid); List<BranchDto> selectBranchNeedingIssueSyncForProject(@Param("projectUuid") String projectUuid);

+ 8
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java View File

public long getNclocSum(DbSession dbSession, @Nullable String projectUuidToExclude) { public long getNclocSum(DbSession dbSession, @Nullable String projectUuidToExclude) {
return Optional.ofNullable(mapper(dbSession).getNclocSum(projectUuidToExclude)).orElse(0L); return Optional.ofNullable(mapper(dbSession).getNclocSum(projectUuidToExclude)).orElse(0L);
} }

public int countIndexedProjects(DbSession session) {
return mapper(session).countIndexedProjects();
}

public int countProjects(DbSession session) {
return mapper(session).countProjects();
}
} }

+ 4
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java View File



@CheckForNull @CheckForNull
Long getNclocSum(@Nullable @Param("projectUuidToExclude") String projectUuidToExclude); Long getNclocSum(@Nullable @Param("projectUuidToExclude") String projectUuidToExclude);

int countIndexedProjects();

int countProjects();
} }

+ 0
- 14
server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml View File

from dual from dual
</select> </select>


<select id="countByNeedIssueSync" parameterType="map" resultType="int">
select
count(pb.uuid)
from project_branches pb
where
pb.need_issue_sync = #{needIssueSync, jdbcType=BOOLEAN}
</select>

<select id="countAll" parameterType="map" resultType="int">
select
count(pb.uuid)
from project_branches pb
</select>

<select id="selectBranchNeedingIssueSync" resultType="org.sonar.db.component.BranchDto"> <select id="selectBranchNeedingIssueSync" resultType="org.sonar.db.component.BranchDto">
select select
<include refid="columns"/> <include refid="columns"/>

+ 14
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml View File

and uuid &lt;&gt; #{projectUuidToExclude,jdbcType=VARCHAR} and uuid &lt;&gt; #{projectUuidToExclude,jdbcType=VARCHAR}
</if> </if>
</select> </select>

<select id="countIndexedProjects" resultType="int">
select count(p.uuid)
from projects p
where p.qualifier = 'TRK'
and not exists (select 1 from project_branches pb where pb.project_uuid = p.uuid and pb.need_issue_sync = ${_true})
</select>

<select id="countProjects" resultType="int">
select count(p.uuid)
from projects p
where p.qualifier = 'TRK'
</select>

</mapper> </mapper>

+ 3
- 3
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndexSyncProgressChecker.java View File

} }


public IssueSyncProgress getIssueSyncProgress(DbSession dbSession) { public IssueSyncProgress getIssueSyncProgress(DbSession dbSession) {
int completed = dbClient.branchDao().countByNeedIssueSync(dbSession, false);
int completedCount = dbClient.projectDao().countIndexedProjects(dbSession);
int total = dbClient.projectDao().countProjects(dbSession);
boolean hasFailures = dbClient.ceActivityDao().hasAnyFailedOrCancelledIssueSyncTask(dbSession); boolean hasFailures = dbClient.ceActivityDao().hasAnyFailedOrCancelledIssueSyncTask(dbSession);
boolean isCompleted = !dbClient.ceQueueDao().hasAnyIssueSyncTaskPendingOrInProgress(dbSession); boolean isCompleted = !dbClient.ceQueueDao().hasAnyIssueSyncTaskPendingOrInProgress(dbSession);
int total = dbClient.branchDao().countAll(dbSession);
return new IssueSyncProgress(isCompleted, completed, total, hasFailures);
return new IssueSyncProgress(isCompleted, completedCount, total, hasFailures);
} }


public void checkIfAnyComponentsNeedIssueSync(DbSession dbSession, List<String> componentKeys) { public void checkIfAnyComponentsNeedIssueSync(DbSession dbSession, List<String> componentKeys) {

+ 6
- 15
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueSyncProgress.java View File

package org.sonar.server.issue.index; package org.sonar.server.issue.index;


public class IssueSyncProgress { public class IssueSyncProgress {
private static final int PERCENT_100 = 100;


private final int completed;
private final int completedCount;
private final int total; private final int total;

private final boolean hasFailures; private final boolean hasFailures;
private final boolean isCompleted; private final boolean isCompleted;


public IssueSyncProgress(boolean isCompleted, int completed, int total, boolean hasFailures) {
this.completed = completed;
public IssueSyncProgress(boolean isCompleted, int completedCount, int total, boolean hasFailures) {
this.completedCount = completedCount;
this.hasFailures = hasFailures; this.hasFailures = hasFailures;
this.isCompleted = isCompleted; this.isCompleted = isCompleted;
this.total = total; this.total = total;
} }


public int getCompleted() {
return completed;
public int getCompletedCount() {
return completedCount;
} }


public boolean hasFailures() { public boolean hasFailures() {
return total; return total;
} }


public int toPercentCompleted() {
if (total != 0) {
return (int) Math.floor(PERCENT_100 * (double) completed / total);
}
return PERCENT_100;
}

public boolean isCompleted() { public boolean isCompleted() {
return completed == total || isCompleted;
return completedCount == total || isCompleted;
} }
} }

+ 13
- 46
server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSyncProgressCheckerTest.java View File

*/ */
package org.sonar.server.issue.index; package org.sonar.server.issue.index;


import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
private final IssueIndexSyncProgressChecker underTest = new IssueIndexSyncProgressChecker(db.getDbClient()); private final IssueIndexSyncProgressChecker underTest = new IssueIndexSyncProgressChecker(db.getDbClient());


@Test @Test
public void return_100_if_there_is_no_tasks_left() {
public void getIssueSyncProgress_whenNoTasksLeft_shouldReturnCompleted() {
IssueSyncProgress issueSyncProgress = underTest.getIssueSyncProgress(db.getSession()); IssueSyncProgress issueSyncProgress = underTest.getIssueSyncProgress(db.getSession());
assertThat(issueSyncProgress.getCompleted()).isZero();
assertThat(issueSyncProgress.getCompletedCount()).isZero();
assertThat(issueSyncProgress.getTotal()).isZero(); assertThat(issueSyncProgress.getTotal()).isZero();
assertThat(issueSyncProgress.toPercentCompleted()).isEqualTo(100);
assertThat(issueSyncProgress.isCompleted()).isTrue(); assertThat(issueSyncProgress.isCompleted()).isTrue();
assertThat(issueSyncProgress.hasFailures()).isFalse(); assertThat(issueSyncProgress.hasFailures()).isFalse();
} }


@Test @Test
public void return_100_if_all_branches_have_need_issue_sync_set_FALSE() {
IntStream.range(0, 13).forEach(value -> insertProjectWithBranches(false, 2));
IntStream.range(0, 14).forEach(value -> insertProjectWithBranches(false, 4));
IntStream.range(0, 4).forEach(value -> insertProjectWithBranches(false, 10));
public void getIssueSyncProgress_whenNoBranchesNeedsIssueSync_shouldReturnCompleted() {
IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 1));
IntStream.range(0, 20).forEach(value -> insertProjectWithBranches(false, 2));


IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession()); IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
assertThat(result.getCompleted()).isEqualTo(153);
assertThat(result.getTotal()).isEqualTo(153);
assertThat(result.toPercentCompleted()).isEqualTo(100);
assertThat(result.getCompletedCount()).isEqualTo(30);
assertThat(result.getTotal()).isEqualTo(30);
assertThat(result.isCompleted()).isTrue(); assertThat(result.isCompleted()).isTrue();
} }


@Test @Test
public void return_has_failure_true_if_exists_task() {
public void getIssueSyncProgress_whenTasksExist_shouldReturnFailures() {
assertThat(underTest.getIssueSyncProgress(db.getSession()).hasFailures()).isFalse(); assertThat(underTest.getIssueSyncProgress(db.getSession()).hasFailures()).isFalse();


ProjectData projectData1 = insertProjectWithBranches(false, 0); ProjectData projectData1 = insertProjectWithBranches(false, 0);
} }


@Test @Test
@UseDataProvider("various_task_numbers")
public void return_correct_percent_value_for_branches_to_sync(int toSync, int synced, int expectedPercent) {
IntStream.range(0, toSync).forEach(value -> insertProjectWithBranches(true, 0));
IntStream.range(0, synced).forEach(value -> insertProjectWithBranches(false, 0));

IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
assertThat(result.getCompleted()).isEqualTo(synced);
assertThat(result.getTotal()).isEqualTo(toSync + synced);
assertThat(result.toPercentCompleted()).isEqualTo(expectedPercent);
}

@DataProvider
public static Object[][] various_task_numbers() {
return new Object[][] {
// toSync, synced, expected result
{0, 0, 100},
{0, 9, 100},
{10, 0, 0},
{99, 1, 1},
{2, 1, 33},
{6, 4, 40},
{7, 7, 50},
{1, 2, 66},
{4, 10, 71},
{1, 99, 99},
};
}

@Test
public void return_0_if_all_branches_have_need_issue_sync_set_true() {
public void getIssueSyncProgress_whenBranchesNeedIssueSync_shouldReturnNotCompleted() {
insertCeQueue("TASK_1", Status.PENDING);
// only project // only project
IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 0)); IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 0));


IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 1)); IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 1));


IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession()); IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
assertThat(result.getCompleted()).isZero();
assertThat(result.getTotal()).isEqualTo(30);
assertThat(result.toPercentCompleted()).isZero();
assertThat(result.getCompletedCount()).isZero();
assertThat(result.getTotal()).isEqualTo(20);
assertThat(result.isCompleted()).isFalse();
} }


@Test @Test

+ 9
- 7
server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/ws/IndexationStatusActionIT.java View File

@Rule @Rule
public DbTester db = DbTester.create(System2.INSTANCE); public DbTester db = DbTester.create(System2.INSTANCE);


private WsActionTester ws = new WsActionTester(new IndexationStatusAction(db.getDbClient(), issueIndexSyncProgressCheckerMock));
private final WsActionTester ws = new WsActionTester(new IndexationStatusAction(db.getDbClient(), issueIndexSyncProgressCheckerMock));


@Test @Test
public void definition() { public void definition() {


@Test @Test
public void verify_example_of_response() { public void verify_example_of_response() {
when(issueIndexSyncProgressCheckerMock.getIssueSyncProgress(any())).thenReturn(new IssueSyncProgress(true,0, 0, false));
when(issueIndexSyncProgressCheckerMock.getIssueSyncProgress(any())).thenReturn(new IssueSyncProgress(false, 22, 38, false));
ws.newRequest().execute().assertJson(ws.getDef().responseExampleAsString()); ws.newRequest().execute().assertJson(ws.getDef().responseExampleAsString());
} }


@Test @Test
public void return_100_if_there_is_no_tasks_left() {
public void call_whenNoTasksLeft_shouldReturnCompleted() {
when(issueIndexSyncProgressCheckerMock.getIssueSyncProgress(any())).thenReturn(new IssueSyncProgress(true, 10, 10, false)); when(issueIndexSyncProgressCheckerMock.getIssueSyncProgress(any())).thenReturn(new IssueSyncProgress(true, 10, 10, false));
IndexationStatusWsResponse response = ws.newRequest() IndexationStatusWsResponse response = ws.newRequest()
.executeProtobuf(IndexationStatusWsResponse.class); .executeProtobuf(IndexationStatusWsResponse.class);
assertThat(response.getPercentCompleted()).isEqualTo(100);
assertThat(response.getCompletedCount()).isEqualTo(10);
assertThat(response.getTotal()).isEqualTo(10);
assertThat(response.getIsCompleted()).isTrue(); assertThat(response.getIsCompleted()).isTrue();
assertThat(response.getHasFailures()).isFalse(); assertThat(response.getHasFailures()).isFalse();
} }


@Test @Test
public void return_0_if_all_branches_have_need_issue_sync_set_TRUE() {
when(issueIndexSyncProgressCheckerMock.getIssueSyncProgress(any())).thenReturn(new IssueSyncProgress(false,0, 10, false));
public void call_whenBranchesNeedIssueSync_shouldReturnNotCompleted() {
when(issueIndexSyncProgressCheckerMock.getIssueSyncProgress(any())).thenReturn(new IssueSyncProgress(false, 0, 10, false));


IndexationStatusWsResponse response = ws.newRequest() IndexationStatusWsResponse response = ws.newRequest()
.executeProtobuf(IndexationStatusWsResponse.class); .executeProtobuf(IndexationStatusWsResponse.class);
assertThat(response.getPercentCompleted()).isZero();
assertThat(response.getCompletedCount()).isZero();
assertThat(response.getTotal()).isEqualTo(10);
assertThat(response.getIsCompleted()).isFalse(); assertThat(response.getIsCompleted()).isFalse();
assertThat(response.getHasFailures()).isFalse(); assertThat(response.getHasFailures()).isFalse();
} }

+ 5
- 2
server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/IndexationStatusAction.java View File

*/ */
package org.sonar.server.ce.ws; package org.sonar.server.ce.ws;


import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
@Override @Override
public void define(WebService.NewController controller) { public void define(WebService.NewController controller) {
controller.createAction("indexation_status") controller.createAction("indexation_status")
.setDescription("Returns percentage of completed issue synchronization.")
.setDescription("Returns the count of projects with completed issue indexation.")
.setResponseExample(getClass().getResource("indexation_status-example.json")) .setResponseExample(getClass().getResource("indexation_status-example.json"))
.setChangelog(new Change("10.2", "Project count is returned instead of branch percentage."))
.setHandler(this) .setHandler(this)
.setInternal(true) .setInternal(true)
.setSince("8.4"); .setSince("8.4");


return IndexationStatusWsResponse.newBuilder() return IndexationStatusWsResponse.newBuilder()
.setIsCompleted(issueSyncProgress.isCompleted()) .setIsCompleted(issueSyncProgress.isCompleted())
.setPercentCompleted(issueSyncProgress.toPercentCompleted())
.setHasFailures(issueSyncProgress.hasFailures()) .setHasFailures(issueSyncProgress.hasFailures())
.setCompletedCount(issueSyncProgress.getCompletedCount())
.setTotal(issueSyncProgress.getTotal())
.build(); .build();
} }



+ 4
- 3
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/ce/ws/indexation_status-example.json View File

{ {
"isCompleted": true,
"percentCompleted": 100,
"hasFailures": false
"isCompleted": false,
"hasFailures": false,
"completedCount": 22,
"total": 38
} }

+ 3
- 2
sonar-ws/src/main/protobuf/ws-ce.proto View File

// GET api/ce/indexation_status // GET api/ce/indexation_status
message IndexationStatusWsResponse { message IndexationStatusWsResponse {
optional bool isCompleted = 1; optional bool isCompleted = 1;
optional int32 percentCompleted = 2;
optional bool hasFailures = 3;
optional bool hasFailures = 2;
optional int32 completedCount = 3;
optional int32 total = 4;
} }


// GET api/ce/analysis_status // GET api/ce/analysis_status

Loading…
Cancel
Save