import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
import static org.sonar.db.component.BranchType.BRANCH;
import static org.sonar.db.component.BranchType.PULL_REQUEST;
assertThat(loaded.isMain()).isTrue();
}
+ @Test
+ public void selectBranchMeasuresForTelemetry() {
+ BranchDto dto = new BranchDto();
+ dto.setProjectUuid("U1");
+ dto.setUuid("U1");
+ dto.setBranchType(BranchType.BRANCH);
+ dto.setKey("feature");
+ dto.setIsMain(true);
+ dto.setExcludeFromPurge(false);
+ underTest.insert(dbSession, dto);
+
+ MetricDto qg = db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY));
+ SnapshotDto analysis = db.components().insertSnapshot(dto);
+ db.measures().insertMeasure(dto, analysis, qg, pm -> pm.setData("OK"));
+
+ var branchMeasures = underTest.selectBranchMeasuresWithCaycMetric(dbSession);
+
+ assertThat(branchMeasures)
+ .hasSize(1)
+ .extracting(BranchMeasuresDto::getBranchUuid, BranchMeasuresDto::getBranchKey, BranchMeasuresDto::getProjectUuid,
+ BranchMeasuresDto::getAnalysisCount, BranchMeasuresDto::getGreenQualityGateCount, BranchMeasuresDto::getExcludeFromPurge)
+ .containsExactly(tuple("U1", "feature", "U1", 1, 1, false));
+ }
+
@Test
public void updateExcludeFromPurge() {
BranchDto dto = new BranchDto();
@DataProvider
public static Object[][] nullOrEmpty() {
- return new Object[][]{
+ return new Object[][] {
{null},
{""}
};
public static Object[][] oldAndNewValuesCombinations() {
String value1 = randomAlphabetic(10);
String value2 = randomAlphabetic(20);
- return new Object[][]{
+ return new Object[][] {
{null, value1},
{"", value1},
{value1, null},
assertThat(branches).extracting(BranchDto::getUuid, BranchDto::getKey, BranchDto::isMain, BranchDto::getProjectUuid, BranchDto::getBranchType, BranchDto::getMergeBranchUuid)
.containsOnly(tuple(mainBranch.getUuid(), mainBranch.getKey(), mainBranch.isMain(), mainBranch.getProjectUuid(), mainBranch.getBranchType(), mainBranch.getMergeBranchUuid()),
- tuple(featureBranch.getUuid(), featureBranch.getKey(), featureBranch.isMain(), featureBranch.getProjectUuid(), featureBranch.getBranchType(), featureBranch.getMergeBranchUuid()));
+ tuple(featureBranch.getUuid(), featureBranch.getKey(), featureBranch.isMain(), featureBranch.getProjectUuid(), featureBranch.getBranchType(),
+ featureBranch.getMergeBranchUuid()));
}
@Test
db.measures().insertLiveMeasure(project3, unanalyzedC);
assertThat(underTest.countPrBranchAnalyzedLanguageByProjectUuid(db.getSession()))
- .extracting(PrBranchAnalyzedLanguageCountByProjectDto::getProjectUuid, PrBranchAnalyzedLanguageCountByProjectDto::getBranch, PrBranchAnalyzedLanguageCountByProjectDto::getPullRequest)
+ .extracting(PrBranchAnalyzedLanguageCountByProjectDto::getProjectUuid, PrBranchAnalyzedLanguageCountByProjectDto::getBranch,
+ PrBranchAnalyzedLanguageCountByProjectDto::getPullRequest)
.containsExactlyInAnyOrder(
tuple(project1.uuid(), 3L, 3L),
tuple(project2.uuid(), 1L, 1L),
@DataProvider
public static Object[][] booleanValues() {
- return new Object[][]{
+ return new Object[][] {
{true},
{false}
};
*/
package org.sonar.db.component;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
.orElse(false);
}
- public List<BranchDto> selectAllBranches(DbSession dbSession) {
- return mapper(dbSession).selectAllBranches();
+ public List<BranchMeasuresDto> selectBranchMeasuresWithCaycMetric(DbSession dbSession) {
+ long yesterday = ZonedDateTime.now(ZoneId.systemDefault()).minusDays(1).toInstant().toEpochMilli();
+ return mapper(dbSession).selectBranchMeasuresWithCaycMetric(yesterday);
}
}
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import org.apache.ibatis.annotations.Param;
public interface BranchMapper {
List<BranchDto> selectMainBranchesByProjectUuids(@Param("projectUuids") Collection<String> projectUuids);
- List<BranchDto> selectAllBranches();
+ List<BranchMeasuresDto> selectBranchMeasuresWithCaycMetric(long yesterday);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+public class BranchMeasuresDto {
+ private String branchUuid;
+ private String projectUuid;
+ private String branchKey;
+ private boolean excludeFromPurge;
+ private int greenQualityGateCount;
+ private int analysisCount;
+
+ public BranchMeasuresDto(String branchUuid, String projectUuid, String branchKey, boolean excludeFromPurge, int greenQualityGateCount, int analysisCount) {
+ this.branchUuid = branchUuid;
+ this.projectUuid = projectUuid;
+ this.branchKey = branchKey;
+ this.excludeFromPurge = excludeFromPurge;
+ this.greenQualityGateCount = greenQualityGateCount;
+ this.analysisCount = analysisCount;
+ }
+
+ public String getBranchUuid() {
+ return branchUuid;
+ }
+
+ public String getProjectUuid() {
+ return projectUuid;
+ }
+
+ public boolean getExcludeFromPurge() {
+ return excludeFromPurge;
+ }
+
+ public int getGreenQualityGateCount() {
+ return greenQualityGateCount;
+ }
+
+ public int getAnalysisCount() {
+ return analysisCount;
+ }
+
+ public String getBranchKey() {
+ return branchKey;
+ }
+
+}
pb.is_main as isMain
</sql>
+ <sql id="telemetryColumns">
+ pb.uuid as branchUuid,
+ pb.project_uuid as projectUuid,
+ pb.kee as branchKey,
+ pb.exclude_from_purge as excludeFromPurge,
+ coalesce(ag.greenQualityGateCount, 0) as greenQualityGateCount,
+ coalesce(ag.analysisCount, 0) as analysisCount
+ </sql>
+
<insert id="insert" parameterType="map" useGeneratedKeys="false">
insert into project_branches (
uuid,
</foreach>
</select>
- <select id="selectAllBranches" resultType="org.sonar.db.component.BranchDto">
+ <select id="selectBranchMeasuresWithCaycMetric" resultType="org.sonar.db.component.BranchMeasuresDto">
select
- <include refid="columns"/>
+ <include refid="telemetryColumns"/>
from project_branches pb
- where branch_type='BRANCH'
+ left join (
+ select pm.component_uuid as branchUuid, count(case when pm.text_value ='OK' then 1 end) as greenQualityGateCount, count(1) as analysisCount
+ from project_measures pm
+ inner join metrics m on m.uuid = pm.metric_uuid
+ inner join snapshots s on s.uuid = pm.analysis_uuid
+ where m.name = 'alert_status' and s.created_at >= #{yesterday, jdbcType=BIGINT}
+ group by pm.component_uuid
+ ) ag
+ on ag.branchUuid = pb.uuid
+ where pb.branch_type='BRANCH'
</select>
<select id="selectByProjectUuid" parameterType="string" resultType="org.sonar.db.component.BranchDto">
}
}
- record Branch(String projectUuid, String branchUuid, int ncdId) {
+ record Branch(String projectUuid, String branchUuid, int ncdId, int greenQualityGateCount, int analysisCount, boolean excludeFromPurge) {
}
record Project(String projectUuid, Long lastAnalysis, String language, Long loc) {
json.prop(PROJECT_ID, branch.projectUuid());
json.prop("branchUuid", branch.branchUuid());
json.prop(NCD_ID, branch.ncdId());
+ json.prop("greenQualityGateCount", branch.greenQualityGateCount());
+ json.prop("analysisCount", branch.analysisCount());
+ json.prop("excludeFromPurge", branch.excludeFromPurge());
json.endObject();
});
json.endArray();
{
"branches": [
{
- "projectUuid": "%s",
- "branchUuid": "%s",
- "ncdId": %s
+ "projectUuid": "projectUuid1",
+ "branchUuid": "branchUuid1",
+ "ncdId": 12345,
+ "greenQualityGateCount": 1,
+ "analysisCount": 2,
+ "excludeFromPurge": true
},
{
- "projectUuid": "%s",
- "branchUuid": "%s",
- "ncdId": %s
- },
+ "projectUuid": "projectUuid2",
+ "branchUuid": "branchUuid2",
+ "ncdId": 12345,
+ "greenQualityGateCount": 0,
+ "analysisCount": 2,
+ "excludeFromPurge": true
+ }
]
-
}
- """.formatted("projectUuid1", "branchUuid1", NCD_ID, "projectUuid2", "branchUuid2", NCD_ID));
+ """);
}
@Test
}
private List<TelemetryData.Branch> attachBranches() {
- return List.of(new TelemetryData.Branch("projectUuid1", "branchUuid1", NCD_ID),
- new TelemetryData.Branch("projectUuid2", "branchUuid2", NCD_ID));
+ return List.of(new TelemetryData.Branch("projectUuid1", "branchUuid1", NCD_ID, 1, 2, true),
+ new TelemetryData.Branch("projectUuid2", "branchUuid2", NCD_ID, 0, 2, true));
}
+
private List<TelemetryData.NewCodeDefinition> attachNewCodeDefinitions() {
return List.of(NCD_INSTANCE, NCD_PROJECT);
}
import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.ProjectAlmKeyAndProject;
import org.sonar.db.component.AnalysisPropertyValuePerProject;
-import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchMeasuresDto;
import org.sonar.db.component.PrBranchAnalyzedLanguageCountByProjectDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.LiveMeasureDto;
getVersion));
data.setPlugins(plugins);
try (DbSession dbSession = dbClient.openSession(false)) {
- var branchDtos = dbClient.branchDao().selectAllBranches(dbSession);
- loadNewCodeDefinitions(dbSession, branchDtos);
+ var branchMeasuresDtos = dbClient.branchDao().selectBranchMeasuresWithCaycMetric(dbSession);
+ loadNewCodeDefinitions(dbSession, branchMeasuresDtos);
data.setDatabase(loadDatabaseMetadata(dbSession));
data.setNcdId(instanceNcd.hashCode());
resolveUnanalyzedLanguageCode(data, dbSession);
resolveProjectStatistics(data, dbSession, defaultQualityGateUuid);
resolveProjects(data, dbSession);
- resolveBranches(data, branchDtos);
+ resolveBranches(data, branchMeasuresDtos);
resolveQualityGates(data, dbSession);
resolveUsers(data, dbSession);
}
.build();
}
- private void resolveBranches(TelemetryData.Builder data, List<BranchDto> branchDtos) {
- var branches = branchDtos.stream()
+ private void resolveBranches(TelemetryData.Builder data, List<BranchMeasuresDto> branchMeasuresDtos) {
+ var branches = branchMeasuresDtos.stream()
.map(dto -> {
var projectNcd = ncdByProject.getOrDefault(dto.getProjectUuid(), instanceNcd);
- var ncdId = ncdByBranch.getOrDefault(dto.getUuid(), projectNcd).hashCode();
- return new TelemetryData.Branch(dto.getProjectUuid(), dto.getUuid(), ncdId);
+ var ncdId = ncdByBranch.getOrDefault(dto.getBranchUuid(), projectNcd).hashCode();
+ return new TelemetryData.Branch(
+ dto.getProjectUuid(), dto.getBranchUuid(), ncdId,
+ dto.getGreenQualityGateCount(), dto.getAnalysisCount(), dto.getExcludeFromPurge());
})
.toList();
data.setBranches(branches);
this.instanceNcd = NewCodeDefinition.getInstanceDefault();
}
- private void loadNewCodeDefinitions(DbSession dbSession, List<BranchDto> branchDtos) {
- var branchUuidByKey = branchDtos.stream().collect(Collectors.toMap(dto -> createBranchUniqueKey(dto.getProjectUuid(), dto.getBranchKey()), BranchDto::getUuid));
+ private void loadNewCodeDefinitions(DbSession dbSession, List<BranchMeasuresDto> branchMeasuresDtos) {
+ var branchUuidByKey = branchMeasuresDtos.stream()
+ .collect(Collectors.toMap(dto -> createBranchUniqueKey(dto.getProjectUuid(), dto.getBranchKey()), BranchMeasuresDto::getBranchUuid));
List<NewCodePeriodDto> newCodePeriodDtos = dbClient.newCodePeriodDao().selectAll(dbSession);
NewCodeDefinition ncd;
boolean hasInstance = false;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import org.sonar.server.property.MapInternalProperties;
import org.sonar.server.qualitygate.QualityGateCaycChecker;
import org.sonar.server.qualitygate.QualityGateFinder;
+import org.sonar.server.telemetry.TelemetryData.Branch;
import org.sonar.server.telemetry.TelemetryData.NewCodeDefinition;
import org.sonar.server.telemetry.TelemetryData.ProjectStatistics;
import org.sonar.updatecenter.common.Version;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY;
import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
Optional.empty(), instanceNcdId));
assertThat(data.getBranches())
- .extracting(TelemetryData.Branch::branchUuid, TelemetryData.Branch::ncdId)
+ .extracting(Branch::branchUuid, Branch::ncdId)
.containsExactlyInAnyOrder(
tuple(branch1.uuid(), projectNcdId),
tuple(branch2.uuid(), branchNcdId),
);
}
+ @Test
+ public void send_branch_measures_data() {
+ Long analysisDate = ZonedDateTime.now(ZoneId.systemDefault()).toInstant().toEpochMilli();
+
+ MetricDto qg = db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY));
+
+ ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
+
+ ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
+
+ SnapshotDto project1Analysis1 = db.components().insertSnapshot(project1, t -> t.setLast(true).setBuildDate(analysisDate));
+ SnapshotDto project1Analysis2 = db.components().insertSnapshot(project1, t -> t.setLast(true).setBuildDate(analysisDate));
+ SnapshotDto project2Analysis = db.components().insertSnapshot(project2, t -> t.setLast(true).setBuildDate(analysisDate));
+ db.measures().insertMeasure(project1, project1Analysis1, qg, pm -> pm.setData("OK"));
+ db.measures().insertMeasure(project1, project1Analysis2, qg, pm -> pm.setData("ERROR"));
+ db.measures().insertMeasure(project2, project2Analysis, qg, pm -> pm.setData("ERROR"));
+
+ var branch1 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("reference"));
+ var branch2 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("custom"));
+
+ db.newCodePeriods().insert(project1.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
+ db.newCodePeriods().insert(project1.uuid(), branch2.branchUuid(), NewCodePeriodType.REFERENCE_BRANCH, "reference");
+
+ var instanceNcdId = NewCodeDefinition.getInstanceDefault().hashCode();
+ var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode();
+ var branchNcdId = new NewCodeDefinition(NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid(), "branch").hashCode();
+
+ TelemetryData data = communityUnderTest.load();
+
+ assertThat(data.getBranches())
+ .extracting(Branch::branchUuid, Branch::ncdId, Branch::greenQualityGateCount, Branch::analysisCount)
+ .containsExactlyInAnyOrder(
+ tuple(branch1.uuid(), projectNcdId, 0, 0),
+ tuple(branch2.uuid(), branchNcdId, 0, 0),
+ tuple(project1.uuid(), projectNcdId, 1, 2),
+ tuple(project2.uuid(), instanceNcdId, 0, 1));
+
+ }
+
private List<UserDto> composeActiveUsers(int count) {
UserDbTester userDbTester = db.users();
Function<Integer, Consumer<UserDto>> userConfigurator = index -> user -> user.setExternalIdentityProvider("provider" + index).setLastSonarlintConnectionDate(index * 2L);
.containsExactlyInAnyOrder(tuple(2L, projectNcdId));
assertThat(data.getBranches())
- .extracting(TelemetryData.Branch::branchUuid, TelemetryData.Branch::ncdId)
+ .extracting(Branch::branchUuid, Branch::ncdId)
.contains(tuple(branch.uuid(), projectNcdId));
}