Co-authored-by: Zipeng WU <zipeng.wu@sonarsource.com> Co-authored-by: Nolwenn Cadic <nolwenn.cadic@sonarsource.com>tags/10.1.0.73491
@@ -198,4 +198,8 @@ public class BranchDao implements Dao { | |||
.map(BranchDto::isNeedIssueSync) | |||
.orElse(false); | |||
} | |||
public List<BranchDto> selectAllBranches(DbSession dbSession) { | |||
return mapper(dbSession).selectAllBranches(); | |||
} | |||
} |
@@ -76,4 +76,6 @@ public interface BranchMapper { | |||
Optional<BranchDto> selectMainBranchByProjectUuid(String projectUuid); | |||
List<BranchDto> selectMainBranchesByProjectUuids(@Param("projectUuids") Collection<String> projectUuids); | |||
List<BranchDto> selectAllBranches(); | |||
} |
@@ -112,4 +112,7 @@ public class NewCodePeriodDao implements Dao { | |||
return session.getMapper(NewCodePeriodMapper.class); | |||
} | |||
public List<NewCodePeriodDto> selectAll(DbSession dbSession) { | |||
return mapper(dbSession).selectAll(); | |||
} | |||
} |
@@ -45,4 +45,6 @@ public interface NewCodePeriodMapper { | |||
long countByProjectAnalysis(String projectAnalysisUuid); | |||
List<NewCodePeriodDto> selectAllByProject(String projectUuid); | |||
List<NewCodePeriodDto> selectAll(); | |||
} |
@@ -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 |
@@ -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"/> |
@@ -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; |
@@ -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)); |
@@ -23,4 +23,6 @@ public interface TelemetryDataLoader { | |||
TelemetryData load(); | |||
String loadServerId(); | |||
void reset(); | |||
} |
@@ -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()) |
@@ -150,6 +150,7 @@ public class TelemetryDaemon extends AbstractStoppableScheduledExecutorServiceIm | |||
dataJsonWriter.writeTelemetryData(json, statistics); | |||
} | |||
telemetryClient.upload(jsonString.toString()); | |||
dataLoader.reset(); | |||
} | |||
private boolean shouldUploadStatistics(long now) { |
@@ -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; |
@@ -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"}, |