Browse Source

SONAR-19297 Track new code definition in Telemetry

Co-authored-by: Zipeng WU <zipeng.wu@sonarsource.com>
Co-authored-by: Nolwenn Cadic <nolwenn.cadic@sonarsource.com>
tags/10.1.0.73491
Zipeng WU 1 year ago
parent
commit
133d65fa22

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

@@ -198,4 +198,8 @@ public class BranchDao implements Dao {
.map(BranchDto::isNeedIssueSync)
.orElse(false);
}

public List<BranchDto> selectAllBranches(DbSession dbSession) {
return mapper(dbSession).selectAllBranches();
}
}

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

@@ -76,4 +76,6 @@ public interface BranchMapper {
Optional<BranchDto> selectMainBranchByProjectUuid(String projectUuid);

List<BranchDto> selectMainBranchesByProjectUuids(@Param("projectUuids") Collection<String> projectUuids);

List<BranchDto> selectAllBranches();
}

+ 3
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodDao.java View File

@@ -112,4 +112,7 @@ public class NewCodePeriodDao implements Dao {
return session.getMapper(NewCodePeriodMapper.class);
}

public List<NewCodePeriodDto> selectAll(DbSession dbSession) {
return mapper(dbSession).selectAll();
}
}

+ 2
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodMapper.java View File

@@ -45,4 +45,6 @@ public interface NewCodePeriodMapper {
long countByProjectAnalysis(String projectAnalysisUuid);

List<NewCodePeriodDto> selectAllByProject(String projectUuid);

List<NewCodePeriodDto> selectAll();
}

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

@@ -100,6 +100,13 @@
</foreach>
</select>

<select id="selectAllBranches" resultType="org.sonar.db.component.BranchDto">
select
<include refid="columns"/>
from project_branches pb
where branch_type='BRANCH'
</select>

<select id="selectByProjectUuid" parameterType="string" resultType="org.sonar.db.component.BranchDto">
select <include refid="columns"/>
from project_branches pb
@@ -107,6 +114,7 @@
pb.project_uuid = #{projectUuid, jdbcType=VARCHAR}
</select>


<select id="selectMainBranchByProjectUuid" resultType="org.sonar.db.component.BranchDto">
select <include refid="columns"/>
from project_branches pb

+ 6
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/newcodeperiod/NewCodePeriodMapper.xml View File

@@ -104,6 +104,12 @@
AND ncp.branch_uuid is null
</select>

<select id="selectAll" parameterType="map" resultType="org.sonar.db.newcodeperiod.NewCodePeriodDto">
SELECT
<include refid="newCodePeriodMapperColumns"/>
FROM new_code_periods ncp
</select>

<select id="selectAllByProject" parameterType="map" resultType="org.sonar.db.newcodeperiod.NewCodePeriodDto">
SELECT
<include refid="newCodePeriodMapperColumns"/>

+ 67
- 0
server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java View File

@@ -20,6 +20,7 @@
package org.sonar.server.telemetry;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -31,6 +32,7 @@ import org.sonar.core.platform.EditionProvider.Edition;
import org.sonar.db.user.UserTelemetryDto;

import static java.util.Objects.requireNonNullElse;
import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION;

public class TelemetryData {
private final String serverId;
@@ -47,9 +49,12 @@ public class TelemetryData {
private final List<UserTelemetryDto> users;
private final List<Project> projects;
private final List<ProjectStatistics> projectStatistics;
private final List<Branch> branches;
private final List<QualityGate> qualityGates;
private final Collection<NewCodeDefinition> newCodeDefinitions;
private final Boolean hasUnanalyzedC;
private final Boolean hasUnanalyzedCpp;
private final int ncdId;
private final Set<String> customSecurityConfigs;

private TelemetryData(Builder builder) {
@@ -71,6 +76,9 @@ public class TelemetryData {
hasUnanalyzedCpp = builder.hasUnanalyzedCpp;
customSecurityConfigs = requireNonNullElse(builder.customSecurityConfigs, Set.of());
managedInstanceInformation = builder.managedInstanceInformation;
ncdId = builder.ncdId;
branches = builder.branches;
newCodeDefinitions = builder.newCodeDefinitions;
}

public String getServerId() {
@@ -149,6 +157,18 @@ public class TelemetryData {
return new Builder();
}

public int getNcdId() {
return ncdId;
}

public List<Branch> getBranches() {
return branches;
}

public Collection<NewCodeDefinition> getNewCodeDefinitions() {
return newCodeDefinitions;
}

static class Builder {
private String serverId;
private String version;
@@ -167,7 +187,10 @@ public class TelemetryData {
private List<UserTelemetryDto> users;
private List<Project> projects;
private List<ProjectStatistics> projectStatistics;
private List<Branch> branches;
private Collection<NewCodeDefinition> newCodeDefinitions;
private List<QualityGate> qualityGates;
private int ncdId;

private Builder() {
// enforce static factory method
@@ -268,15 +291,46 @@ public class TelemetryData {
return this;
}

Builder setNcdId(int ncdId) {
this.ncdId = ncdId;
return this;
}

private static void requireNonNullValues(Object... values) {
Arrays.stream(values).forEach(Objects::requireNonNull);
}

Builder setBranches(List<Branch> branches) {
this.branches = branches;
return this;
}

Builder setNewCodeDefinitions(Collection<NewCodeDefinition> newCodeDefinitions) {
this.newCodeDefinitions = newCodeDefinitions;
return this;
}
}

record Database(String name, String version) {
}

record NewCodeDefinition(String type, @Nullable String value, String scope) {

private static final NewCodeDefinition instanceDefault = new NewCodeDefinition(PREVIOUS_VERSION.name(), "", "instance");

public static NewCodeDefinition getInstanceDefault() {
return instanceDefault;
}

@Override
public String value() {
return value == null ? "" : value;
}
}

record Branch(String projectUuid, String branchUuid, int ncdId) {
}

record Project(String projectUuid, Long lastAnalysis, String language, Long loc) {
}

@@ -300,6 +354,8 @@ public class TelemetryData {
private final Long technicalDebt;
private final Long developmentCost;

private final int ncdId;

ProjectStatistics(Builder builder) {
this.projectUuid = builder.projectUuid;
this.branchCount = builder.branchCount;
@@ -313,6 +369,11 @@ public class TelemetryData {
this.securityHotspots = builder.securityHotspots;
this.technicalDebt = builder.technicalDebt;
this.developmentCost = builder.developmentCost;
this.ncdId = builder.ncdId;
}

public int getNcdId() {
return ncdId;
}

public String getProjectUuid() {
@@ -376,12 +437,18 @@ public class TelemetryData {
private Long securityHotspots;
private Long technicalDebt;
private Long developmentCost;
private int ncdId;

public Builder setProjectUuid(String projectUuid) {
this.projectUuid = projectUuid;
return this;
}

public Builder setNcdId(int ncdId) {
this.ncdId = ncdId;
return this;
}

public Builder setBranchCount(Long branchCount) {
this.branchCount = branchCount;
return this;

+ 39
- 2
server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java View File

@@ -40,6 +40,8 @@ public class TelemetryDataJsonWriter {

private static final String LANGUAGE_PROPERTY = "language";
private static final String VERSION = "version";
private static final String NCD_ID = "ncdId";
private static final String PROJECT_ID = "projectUuid";

private final List<TelemetryExtension> extensions;

@@ -56,6 +58,7 @@ public class TelemetryDataJsonWriter {
json.prop(VERSION, telemetryData.getVersion());
json.prop("messageSequenceNumber", telemetryData.getMessageSequenceNumber());
json.prop("localTimestamp", toUtc(system2.now()));
json.prop(NCD_ID, telemetryData.getNcdId());
telemetryData.getEdition().ifPresent(e -> json.prop("edition", e.name().toLowerCase(Locale.ENGLISH)));
json.prop("defaultQualityGate", telemetryData.getDefaultQualityGate());
json.name("database");
@@ -94,6 +97,8 @@ public class TelemetryDataJsonWriter {
writeUserData(json, telemetryData);
writeProjectData(json, telemetryData);
writeProjectStatsData(json, telemetryData);
writeBranches(json, telemetryData);
writeNewCodeDefinitions(json, telemetryData);
writeQualityGates(json, telemetryData);
writeManagedInstanceInformation(json, telemetryData.getManagedInstanceInformation());
extensions.forEach(e -> e.write(json));
@@ -131,7 +136,7 @@ public class TelemetryDataJsonWriter {
json.beginArray();
telemetryData.getProjects().forEach(project -> {
json.beginObject();
json.prop("projectUuid", project.projectUuid());
json.prop(PROJECT_ID, project.projectUuid());
if (project.lastAnalysis() != null) {
json.prop("lastAnalysis", toUtc(project.lastAnalysis()));
}
@@ -143,19 +148,51 @@ public class TelemetryDataJsonWriter {
}
}

private static void writeBranches(JsonWriter json, TelemetryData telemetryData) {
if (telemetryData.getBranches() != null) {
json.name("branches");
json.beginArray();
telemetryData.getBranches().forEach(branch -> {
json.beginObject();
json.prop(PROJECT_ID, branch.projectUuid());
json.prop("branchUuid", branch.branchUuid());
json.prop(NCD_ID, branch.ncdId());
json.endObject();
});
json.endArray();
}
}

private static void writeNewCodeDefinitions(JsonWriter json, TelemetryData telemetryData) {
if (telemetryData.getNewCodeDefinitions() != null) {
json.name("new-code-definitions");
json.beginArray();
telemetryData.getNewCodeDefinitions().forEach(ncd -> {
json.beginObject();
json.prop(NCD_ID, ncd.hashCode());
json.prop("type", ncd.type());
json.prop("value", ncd.value());
json.prop("scope", ncd.scope());
json.endObject();
});
json.endArray();
}
}

private static void writeProjectStatsData(JsonWriter json, TelemetryData telemetryData) {
if (telemetryData.getProjectStatistics() != null) {
json.name("projects-general-stats");
json.beginArray();
telemetryData.getProjectStatistics().forEach(project -> {
json.beginObject();
json.prop("projectUuid", project.getProjectUuid());
json.prop(PROJECT_ID, project.getProjectUuid());
json.prop("branchCount", project.getBranchCount());
json.prop("pullRequestCount", project.getPullRequestCount());
json.prop("qualityGate", project.getQualityGate());
json.prop("scm", project.getScm());
json.prop("ci", project.getCi());
json.prop("devopsPlatform", project.getDevopsPlatform());
json.prop(NCD_ID, project.getNcdId());
project.getBugs().ifPresent(bugs -> json.prop("bugs", bugs));
project.getVulnerabilities().ifPresent(vulnerabilities -> json.prop("vulnerabilities", vulnerabilities));
project.getSecurityHotspots().ifPresent(securityHotspots -> json.prop("securityHotspots", securityHotspots));

+ 2
- 0
server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataLoader.java View File

@@ -23,4 +23,6 @@ public interface TelemetryDataLoader {
TelemetryData load();

String loadServerId();

void reset();
}

+ 91
- 5
server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java View File

@@ -48,6 +48,8 @@ import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS;
import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION;
import static org.sonar.test.JsonAssert.assertJson;

@RunWith(DataProviderRunner.class)
@@ -61,6 +63,13 @@ public class TelemetryDataJsonWriterTest {

private final TelemetryDataJsonWriter underTest = new TelemetryDataJsonWriter(List.of(extension), system2);

private static final int NCD_ID = 12345;

private static final TelemetryData.NewCodeDefinition NCD_INSTANCE =
new TelemetryData.NewCodeDefinition(PREVIOUS_VERSION.name(), "", "instance");
private static final TelemetryData.NewCodeDefinition NCD_PROJECT =
new TelemetryData.NewCodeDefinition(NUMBER_OF_DAYS.name(), "30", "project");

@Test
public void write_server_id_version_and_sequence() {
TelemetryData data = telemetryBuilder().build();
@@ -414,7 +423,8 @@ public class TelemetryDataJsonWriterTest {
"vulnerabilities": 3,
"securityHotspots": 4,
"technicalDebt": 60,
"developmentCost": 30
"developmentCost": 30,
"ncdId": 12345
},
{
"projectUuid": "uuid-1",
@@ -428,7 +438,8 @@ public class TelemetryDataJsonWriterTest {
"vulnerabilities": 6,
"securityHotspots": 8,
"technicalDebt": 120,
"developmentCost": 60
"developmentCost": 60,
"ncdId": 12345
},
{
"projectUuid": "uuid-2",
@@ -442,7 +453,8 @@ public class TelemetryDataJsonWriterTest {
"vulnerabilities": 9,
"securityHotspots": 12,
"technicalDebt": 180,
"developmentCost": 90
"developmentCost": 90,
"ncdId": 12345
}
]
}
@@ -497,6 +509,70 @@ public class TelemetryDataJsonWriterTest {
);
}

@Test
public void writes_all_branches() {
TelemetryData data = telemetryBuilder()
.setBranches(attachBranches())
.build();

String json = writeTelemetryData(data);
assertJson(json).isSimilarTo("""
{
"branches": [
{
"projectUuid": "%s",
"branchUuid": "%s",
"ncdId": %s
},
{
"projectUuid": "%s",
"branchUuid": "%s",
"ncdId": %s
},
]

}
""".formatted("projectUuid1", "branchUuid1", NCD_ID, "projectUuid2", "branchUuid2", NCD_ID));
}

@Test
public void writes_new_code_definitions() {
TelemetryData data = telemetryBuilder()
.setNewCodeDefinitions(attachNewCodeDefinitions())
.build();

String json = writeTelemetryData(data);
assertJson(json).isSimilarTo("""
{
"new-code-definitions": [
{
"ncdId": %s,
"type": "%s",
"value": "%s",
"scope": "%s"
},
{
"ncdId": %s,
"type": "%s",
"value": "%s",
"scope": "%s"
},
]

}
""".formatted(NCD_INSTANCE.hashCode(), NCD_INSTANCE.type(), NCD_INSTANCE.value(), NCD_INSTANCE.scope(), NCD_PROJECT.hashCode(),
NCD_PROJECT.type(), NCD_PROJECT.value(), NCD_PROJECT.scope()));
}

@Test
public void writes_instance_new_code_definition() {
TelemetryData data = telemetryBuilder().build();

String json = writeTelemetryData(data);
assertThat(json).contains("ncdId");

}

private static TelemetryData.Builder telemetryBuilder() {
return TelemetryData.builder()
.setServerId("foo")
@@ -504,7 +580,8 @@ public class TelemetryDataJsonWriterTest {
.setMessageSequenceNumber(1L)
.setPlugins(Collections.emptyMap())
.setManagedInstanceInformation(new TelemetryData.ManagedInstanceInformation(false, null))
.setDatabase(new TelemetryData.Database("H2", "11"));
.setDatabase(new TelemetryData.Database("H2", "11"))
.setNcdId(NCD_ID);
}

@NotNull
@@ -535,7 +612,8 @@ public class TelemetryDataJsonWriterTest {
.setPRCount((i + 1L) * 2L)
.setQG("qg-" + i).setCi("ci-" + i)
.setScm("scm-" + i)
.setDevops("devops-" + i);
.setDevops("devops-" + i)
.setNcdId(NCD_ID);
}

private static TelemetryData.ProjectStatistics.Builder getProjectStatisticsWithMetricBuilder(int i) {
@@ -553,6 +631,14 @@ public class TelemetryDataJsonWriterTest {
new TelemetryData.QualityGate("uuid-2", "over-compliant"));
}

private List<TelemetryData.Branch> attachBranches() {
return List.of(new TelemetryData.Branch("projectUuid1", "branchUuid1", NCD_ID),
new TelemetryData.Branch("projectUuid2", "branchUuid2", NCD_ID));
}
private List<TelemetryData.NewCodeDefinition> attachNewCodeDefinitions() {
return List.of(NCD_INSTANCE, NCD_PROJECT);
}

@DataProvider
public static Object[][] allEditions() {
return Arrays.stream(EditionProvider.Edition.values())

+ 1
- 0
server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java View File

@@ -150,6 +150,7 @@ public class TelemetryDaemon extends AbstractStoppableScheduledExecutorServiceIm
dataJsonWriter.writeTelemetryData(json, statistics);
}
telemetryClient.upload(jsonString.toString());
dataLoader.reset();
}

private boolean shouldUploadStatistics(long now) {

+ 70
- 4
server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java View File

@@ -27,12 +27,14 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.sonar.api.config.Configuration;
import org.sonar.api.platform.Server;
@@ -46,11 +48,13 @@ import org.sonar.db.DbSession;
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.PrBranchAnalyzedLanguageCountByProjectDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.ProjectLocDistributionDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.newcodeperiod.NewCodePeriodDto;
import org.sonar.db.qualitygate.ProjectQgateAssociationDto;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.management.ManagedInstanceService;
@@ -59,6 +63,7 @@ import org.sonar.server.property.InternalProperties;
import org.sonar.server.qualitygate.QualityGateCaycChecker;
import org.sonar.server.qualitygate.QualityGateFinder;
import org.sonar.server.telemetry.TelemetryData.Database;
import org.sonar.server.telemetry.TelemetryData.NewCodeDefinition;

import static java.util.Arrays.asList;
import static java.util.Optional.ofNullable;
@@ -77,6 +82,7 @@ import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETEC
import static org.sonar.core.platform.EditionProvider.Edition.COMMUNITY;
import static org.sonar.core.platform.EditionProvider.Edition.DATACENTER;
import static org.sonar.core.platform.EditionProvider.Edition.ENTERPRISE;
import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY;
import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_C_KEY;
import static org.sonar.server.telemetry.TelemetryDaemon.I_PROP_MESSAGE_SEQUENCE;
@@ -103,6 +109,10 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
private final QualityGateCaycChecker qualityGateCaycChecker;
private final QualityGateFinder qualityGateFinder;
private final ManagedInstanceService managedInstanceService;
private final Set<NewCodeDefinition> newCodeDefinitions = new HashSet<>();
private final Map<String, NewCodeDefinition> ncdByProject = new HashMap<>();
private final Map<String, NewCodeDefinition> ncdByBranch = new HashMap<>();
private NewCodeDefinition instanceNcd = NewCodeDefinition.getInstanceDefault();

@Inject
public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository,
@@ -143,7 +153,12 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
getVersion));
data.setPlugins(plugins);
try (DbSession dbSession = dbClient.openSession(false)) {
var branchDtos = dbClient.branchDao().selectAllBranches(dbSession);
loadNewCodeDefinitions(dbSession, branchDtos);

data.setDatabase(loadDatabaseMetadata(dbSession));
data.setNcdId(instanceNcd.hashCode());
data.setNewCodeDefinitions(newCodeDefinitions);

String defaultQualityGateUuid = qualityGateFinder.getDefault(dbSession).getUuid();

@@ -151,6 +166,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
resolveUnanalyzedLanguageCode(data, dbSession);
resolveProjectStatistics(data, dbSession, defaultQualityGateUuid);
resolveProjects(data, dbSession);
resolveBranches(data, branchDtos);
resolveQualityGates(data, dbSession);
resolveUsers(data, dbSession);
}
@@ -161,7 +177,6 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
installationDateProperty.ifPresent(s -> data.setInstallationDate(Long.valueOf(s)));
Optional<String> installationVersionProperty = internalProperties.read(InternalProperties.INSTALLATION_VERSION);


return data
.setInstallationVersion(installationVersionProperty.orElse(null))
.setInDocker(dockerSupport.isRunningInDocker())
@@ -169,6 +184,59 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
.build();
}

private void resolveBranches(TelemetryData.Builder data, List<BranchDto> branchDtos) {
var branches = branchDtos.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);
})
.toList();
data.setBranches(branches);
}

@Override
public void reset() {
this.newCodeDefinitions.clear();
this.ncdByBranch.clear();
this.ncdByProject.clear();
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));
List<NewCodePeriodDto> newCodePeriodDtos = dbClient.newCodePeriodDao().selectAll(dbSession);
NewCodeDefinition ncd;
boolean hasInstance = false;
for (var dto : newCodePeriodDtos) {
String projectUuid = dto.getProjectUuid();
String branchUuid = dto.getBranchUuid();
if (branchUuid == null && projectUuid == null) {
ncd = new NewCodeDefinition(dto.getType().name(), dto.getValue(), "instance");
this.instanceNcd = ncd;
hasInstance = true;
} else if (projectUuid != null) {
var value = dto.getType() == REFERENCE_BRANCH ? branchUuidByKey.get(createBranchUniqueKey(projectUuid, dto.getValue())) : dto.getValue();
if (branchUuid == null) {
ncd = new NewCodeDefinition(dto.getType().name(), value, "project");
this.ncdByProject.put(projectUuid, ncd);
} else {
ncd = new NewCodeDefinition(dto.getType().name(), value, "branch");
this.ncdByBranch.put(branchUuid, ncd);
}
} else {
throw new IllegalStateException(String.format("Error in loading telemetry data. New code definition for branch %s doesn't have a projectUuid", branchUuid));
}
this.newCodeDefinitions.add(ncd);
}
if (!hasInstance) {
this.newCodeDefinitions.add(NewCodeDefinition.getInstanceDefault());
}
}

private static String createBranchUniqueKey(String projectUuid, @Nullable String branchKey) {
return projectUuid + "-" + branchKey;
}

private void resolveUnanalyzedLanguageCode(TelemetryData.Builder data, DbSession dbSession) {
long numberOfUnanalyzedCMeasures = dbClient.liveMeasureDao().countProjectsHavingMeasure(dbSession, UNANALYZED_C_KEY);
@@ -216,6 +284,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
.setVulnerabilities(metrics.getOrDefault("vulnerabilities", null))
.setSecurityHotspots(metrics.getOrDefault("security_hotspots", null))
.setTechnicalDebt(metrics.getOrDefault("sqale_index", null))
.setNcdId(ncdByProject.getOrDefault(projectUuid, instanceNcd).hashCode())
.build();
projectStatistics.add(stats);
}
@@ -361,9 +430,6 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
return configuration.get(property).isPresent();
}

private boolean isScimEnabled() {
return this.internalProperties.read(SCIM_PROPERTY_ENABLED).map(Boolean::parseBoolean).orElse(false);
}

private TelemetryData.ManagedInstanceInformation buildManagedInstanceInformation() {
String provider = managedInstanceService.isInstanceExternallyManaged() ? managedInstanceService.getProviderName() : null;

+ 46
- 13
server/sonar-webserver-core/src/test/java/org/sonar/server/telemetry/TelemetryDataLoaderImplTest.java View File

@@ -48,6 +48,8 @@ import org.sonar.db.component.AnalysisPropertyDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.newcodeperiod.NewCodePeriodDto;
import org.sonar.db.newcodeperiod.NewCodePeriodType;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.db.user.UserDbTester;
import org.sonar.db.user.UserDto;
@@ -58,6 +60,8 @@ import org.sonar.server.property.InternalProperties;
import org.sonar.server.property.MapInternalProperties;
import org.sonar.server.qualitygate.QualityGateCaycChecker;
import org.sonar.server.qualitygate.QualityGateFinder;
import org.sonar.server.telemetry.TelemetryData.NewCodeDefinition;
import org.sonar.server.telemetry.TelemetryData.ProjectStatistics;
import org.sonar.updatecenter.common.Version;

import static java.util.Arrays.asList;
@@ -107,9 +111,9 @@ public class TelemetryDataLoaderImplTest {
private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);

private final TelemetryDataLoader communityUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, editionProvider,
internalProperties, configuration, dockerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService);
internalProperties, configuration, dockerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService);
private final TelemetryDataLoader commercialUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, editionProvider,
internalProperties, configuration, dockerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService);
internalProperties, configuration, dockerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService);

private QualityGateDto builtInDefaultQualityGate;
private MetricDto bugsDto;
@@ -199,11 +203,22 @@ public class TelemetryDataLoaderImplTest {
// link one project to a non-default QG
db.qualityGates().associateProjectToQualityGate(db.components().getProjectDtoByMainBranch(project1), qualityGate1);

var branch1 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("reference"));
var branch2 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("custom"));

var ncd1 = db.newCodePeriods().insert(project1.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
var ncd2 = 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.getServerId()).isEqualTo(serverId);
assertThat(data.getVersion()).isEqualTo(version);
assertThat(data.getEdition()).contains(DEVELOPER);
assertThat(data.getDefaultQualityGate()).isEqualTo(builtInDefaultQualityGate.getUuid());
assertThat(data.getNcdId()).isEqualTo(NewCodeDefinition.getInstanceDefault().hashCode());
assertThat(data.getMessageSequenceNumber()).isOne();
assertDatabaseMetadata(data.getDatabase());
assertThat(data.getPlugins()).containsOnly(
@@ -226,13 +241,31 @@ public class TelemetryDataLoaderImplTest {
tuple(project2.uuid(), "java", 180L, analysisDate),
tuple(project2.uuid(), "js", 20L, analysisDate));
assertThat(data.getProjectStatistics())
.extracting(TelemetryData.ProjectStatistics::getBranchCount, TelemetryData.ProjectStatistics::getPullRequestCount, TelemetryData.ProjectStatistics::getQualityGate,
TelemetryData.ProjectStatistics::getScm, TelemetryData.ProjectStatistics::getCi, TelemetryData.ProjectStatistics::getDevopsPlatform,
TelemetryData.ProjectStatistics::getBugs, TelemetryData.ProjectStatistics::getVulnerabilities, TelemetryData.ProjectStatistics::getSecurityHotspots,
TelemetryData.ProjectStatistics::getDevelopmentCost, TelemetryData.ProjectStatistics::getTechnicalDebt)
.extracting(ProjectStatistics::getBranchCount, ProjectStatistics::getPullRequestCount, ProjectStatistics::getQualityGate,
ProjectStatistics::getScm, ProjectStatistics::getCi, ProjectStatistics::getDevopsPlatform,
ProjectStatistics::getBugs, ProjectStatistics::getVulnerabilities, ProjectStatistics::getSecurityHotspots,
ProjectStatistics::getDevelopmentCost, ProjectStatistics::getTechnicalDebt, ProjectStatistics::getNcdId)
.containsExactlyInAnyOrder(
tuple(3L, 0L, qualityGate1.getUuid(), "scm-1", "ci-1", "azure_devops_cloud", Optional.of(1L), Optional.of(1L), Optional.of(1L), Optional.of(50L), Optional.of(5L),
projectNcdId),
tuple(1L, 0L, builtInDefaultQualityGate.getUuid(), "scm-2", "ci-2", "github_cloud", Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(),
Optional.empty(), instanceNcdId));

assertThat(data.getBranches())
.extracting(TelemetryData.Branch::branchUuid, TelemetryData.Branch::ncdId)
.containsExactlyInAnyOrder(
tuple(branch1.uuid(), projectNcdId),
tuple(branch2.uuid(), branchNcdId),
tuple(project1.uuid(), projectNcdId),
tuple(project2.uuid(), instanceNcdId));

assertThat(data.getNewCodeDefinitions())
.extracting(NewCodeDefinition::scope, NewCodeDefinition::type, NewCodeDefinition::value)
.containsExactlyInAnyOrder(
tuple(1L, 0L, qualityGate1.getUuid(), "scm-1", "ci-1", "azure_devops_cloud", Optional.of(1L), Optional.of(1L), Optional.of(1L), Optional.of(50L), Optional.of(5L)),
tuple(1L, 0L, builtInDefaultQualityGate.getUuid(), "scm-2", "ci-2", "github_cloud", Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()));
tuple("instance", NewCodePeriodType.PREVIOUS_VERSION.name(), ""),
tuple("project", NewCodePeriodType.NUMBER_OF_DAYS.name(), "30"),
tuple("branch", NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid()));

assertThat(data.getQualityGates())
.extracting(TelemetryData.QualityGate::uuid, TelemetryData.QualityGate::caycStatus)
.containsExactlyInAnyOrder(
@@ -297,8 +330,8 @@ public class TelemetryDataLoaderImplTest {
tuple(project.uuid(), "js", 50L),
tuple(project.uuid(), "kotlin", 30L));
assertThat(data.getProjectStatistics())
.extracting(TelemetryData.ProjectStatistics::getBranchCount, TelemetryData.ProjectStatistics::getPullRequestCount,
TelemetryData.ProjectStatistics::getScm, TelemetryData.ProjectStatistics::getCi)
.extracting(ProjectStatistics::getBranchCount, ProjectStatistics::getPullRequestCount,
ProjectStatistics::getScm, ProjectStatistics::getCi)
.containsExactlyInAnyOrder(
tuple(2L, 0L, "undetected", "undetected"));
}
@@ -436,7 +469,7 @@ public class TelemetryDataLoaderImplTest {
db.components().insertPublicProject().getMainBranchComponent();
TelemetryData data = communityUnderTest.load();
assertThat(data.getProjectStatistics())
.extracting(TelemetryData.ProjectStatistics::getDevopsPlatform, TelemetryData.ProjectStatistics::getScm, TelemetryData.ProjectStatistics::getCi)
.extracting(ProjectStatistics::getDevopsPlatform, ProjectStatistics::getScm, ProjectStatistics::getCi)
.containsExactlyInAnyOrder(tuple("undetected", "undetected", "undetected"));
}

@@ -460,7 +493,7 @@ public class TelemetryDataLoaderImplTest {
db.qualityGates().setDefaultQualityGate(qualityGate);
TelemetryData data = communityUnderTest.load();
assertThat(data.getProjectStatistics())
.extracting(TelemetryData.ProjectStatistics::getQualityGate)
.extracting(ProjectStatistics::getQualityGate)
.containsOnly(qualityGate.getUuid());
}

@@ -489,7 +522,7 @@ public class TelemetryDataLoaderImplTest {

@DataProvider
public static Object[][] getManagedInstanceData() {
return new Object[][]{
return new Object[][] {
{true, "scim"},
{true, "github"},
{true, "gitlab"},

Loading…
Cancel
Save