You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TelemetryDataLoaderImpl.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.telemetry;
  21. import java.sql.DatabaseMetaData;
  22. import java.sql.SQLException;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.Collection;
  26. import java.util.Collections;
  27. import java.util.HashMap;
  28. import java.util.HashSet;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.Optional;
  32. import java.util.Set;
  33. import java.util.function.Function;
  34. import java.util.stream.Collectors;
  35. import javax.annotation.Nullable;
  36. import javax.inject.Inject;
  37. import org.sonar.api.config.Configuration;
  38. import org.sonar.api.platform.Server;
  39. import org.sonar.api.server.ServerSide;
  40. import org.sonar.core.platform.PlatformEditionProvider;
  41. import org.sonar.core.platform.PluginInfo;
  42. import org.sonar.core.platform.PluginRepository;
  43. import org.sonar.db.DbClient;
  44. import org.sonar.db.DbSession;
  45. import org.sonar.db.alm.setting.ALM;
  46. import org.sonar.db.alm.setting.ProjectAlmKeyAndProject;
  47. import org.sonar.db.component.AnalysisPropertyValuePerProject;
  48. import org.sonar.db.component.BranchMeasuresDto;
  49. import org.sonar.db.component.PrBranchAnalyzedLanguageCountByProjectDto;
  50. import org.sonar.db.component.SnapshotDto;
  51. import org.sonar.db.measure.ProjectLocDistributionDto;
  52. import org.sonar.db.measure.ProjectMainBranchLiveMeasureDto;
  53. import org.sonar.db.metric.MetricDto;
  54. import org.sonar.db.newcodeperiod.NewCodePeriodDto;
  55. import org.sonar.db.project.ProjectDto;
  56. import org.sonar.db.property.PropertyDto;
  57. import org.sonar.db.property.PropertyQuery;
  58. import org.sonar.db.qualitygate.ProjectQgateAssociationDto;
  59. import org.sonar.db.qualitygate.QualityGateConditionDto;
  60. import org.sonar.db.qualitygate.QualityGateDto;
  61. import org.sonar.server.management.ManagedInstanceService;
  62. import org.sonar.server.platform.ContainerSupport;
  63. import org.sonar.server.property.InternalProperties;
  64. import org.sonar.server.qualitygate.Condition;
  65. import org.sonar.server.qualitygate.QualityGateCaycChecker;
  66. import org.sonar.server.qualitygate.QualityGateFinder;
  67. import org.sonar.server.telemetry.TelemetryData.Database;
  68. import org.sonar.server.telemetry.TelemetryData.NewCodeDefinition;
  69. import static java.util.Arrays.asList;
  70. import static java.util.Optional.ofNullable;
  71. import static java.util.stream.Collectors.groupingBy;
  72. import static java.util.stream.Collectors.toMap;
  73. import static org.sonar.api.internal.apachecommons.lang.StringUtils.startsWithIgnoreCase;
  74. import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
  75. import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
  76. import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
  77. import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
  78. import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
  79. import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
  80. import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
  81. import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI;
  82. import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM;
  83. import static org.sonar.core.platform.EditionProvider.Edition.COMMUNITY;
  84. import static org.sonar.core.platform.EditionProvider.Edition.DATACENTER;
  85. import static org.sonar.core.platform.EditionProvider.Edition.ENTERPRISE;
  86. import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
  87. import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY;
  88. import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_C_KEY;
  89. import static org.sonar.server.qualitygate.Condition.Operator.fromDbValue;
  90. import static org.sonar.server.telemetry.TelemetryDaemon.I_PROP_MESSAGE_SEQUENCE;
  91. @ServerSide
  92. public class TelemetryDataLoaderImpl implements TelemetryDataLoader {
  93. private static final String UNDETECTED = "undetected";
  94. public static final String EXTERNAL_SECURITY_REPORT_EXPORTED_AT = "project.externalSecurityReportExportedAt";
  95. private static final Map<String, String> LANGUAGES_BY_SECURITY_JSON_PROPERTY_MAP = Map.of(
  96. "sonar.security.config.javasecurity", "java",
  97. "sonar.security.config.phpsecurity", "php",
  98. "sonar.security.config.pythonsecurity", "python",
  99. "sonar.security.config.roslyn.sonaranalyzer.security.cs", "csharp");
  100. private final Server server;
  101. private final DbClient dbClient;
  102. private final PluginRepository pluginRepository;
  103. private final PlatformEditionProvider editionProvider;
  104. private final Configuration configuration;
  105. private final InternalProperties internalProperties;
  106. private final ContainerSupport containerSupport;
  107. private final QualityGateCaycChecker qualityGateCaycChecker;
  108. private final QualityGateFinder qualityGateFinder;
  109. private final ManagedInstanceService managedInstanceService;
  110. private final CloudUsageDataProvider cloudUsageDataProvider;
  111. private final QualityProfileDataProvider qualityProfileDataProvider;
  112. private final Set<NewCodeDefinition> newCodeDefinitions = new HashSet<>();
  113. private final Map<String, NewCodeDefinition> ncdByProject = new HashMap<>();
  114. private final Map<String, NewCodeDefinition> ncdByBranch = new HashMap<>();
  115. private final Map<String, String> defaultQualityProfileByLanguage = new HashMap<>();
  116. private final Map<ProjectLanguageKey, String> qualityProfileByProjectAndLanguage = new HashMap<>();
  117. private NewCodeDefinition instanceNcd = NewCodeDefinition.getInstanceDefault();
  118. @Inject
  119. public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository,
  120. PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration,
  121. ContainerSupport containerSupport, QualityGateCaycChecker qualityGateCaycChecker, QualityGateFinder qualityGateFinder,
  122. ManagedInstanceService managedInstanceService, CloudUsageDataProvider cloudUsageDataProvider, QualityProfileDataProvider qualityProfileDataProvider) {
  123. this.server = server;
  124. this.dbClient = dbClient;
  125. this.pluginRepository = pluginRepository;
  126. this.editionProvider = editionProvider;
  127. this.internalProperties = internalProperties;
  128. this.configuration = configuration;
  129. this.containerSupport = containerSupport;
  130. this.qualityGateCaycChecker = qualityGateCaycChecker;
  131. this.qualityGateFinder = qualityGateFinder;
  132. this.managedInstanceService = managedInstanceService;
  133. this.cloudUsageDataProvider = cloudUsageDataProvider;
  134. this.qualityProfileDataProvider = qualityProfileDataProvider;
  135. }
  136. private static Database loadDatabaseMetadata(DbSession dbSession) {
  137. try {
  138. DatabaseMetaData metadata = dbSession.getConnection().getMetaData();
  139. return new Database(metadata.getDatabaseProductName(), metadata.getDatabaseProductVersion());
  140. } catch (SQLException e) {
  141. throw new IllegalStateException("Fail to get DB metadata", e);
  142. }
  143. }
  144. @Override
  145. public TelemetryData load() {
  146. TelemetryData.Builder data = TelemetryData.builder();
  147. data.setMessageSequenceNumber(retrieveCurrentMessageSequenceNumber() + 1);
  148. data.setServerId(server.getId());
  149. data.setVersion(server.getVersion());
  150. data.setEdition(editionProvider.get().orElse(null));
  151. Function<PluginInfo, String> getVersion = plugin -> plugin.getVersion() == null ? "undefined" : plugin.getVersion().getName();
  152. Map<String, String> plugins = pluginRepository.getPluginInfos().stream().collect(toMap(PluginInfo::getKey, getVersion));
  153. data.setPlugins(plugins);
  154. try (DbSession dbSession = dbClient.openSession(false)) {
  155. var branchMeasuresDtos = dbClient.branchDao().selectBranchMeasuresWithCaycMetric(dbSession);
  156. loadNewCodeDefinitions(dbSession, branchMeasuresDtos);
  157. loadQualityProfiles(dbSession);
  158. data.setDatabase(loadDatabaseMetadata(dbSession));
  159. data.setNcdId(instanceNcd.hashCode());
  160. data.setNewCodeDefinitions(newCodeDefinitions);
  161. String defaultQualityGateUuid = qualityGateFinder.getDefault(dbSession).getUuid();
  162. String sonarWayQualityGateUuid = qualityGateFinder.getSonarWay(dbSession).getUuid();
  163. List<ProjectDto> projects = dbClient.projectDao().selectProjects(dbSession);
  164. data.setDefaultQualityGate(defaultQualityGateUuid);
  165. data.setSonarWayQualityGate(sonarWayQualityGateUuid);
  166. resolveUnanalyzedLanguageCode(data, dbSession);
  167. resolveProjectStatistics(data, dbSession, defaultQualityGateUuid, projects);
  168. resolveProjects(data, dbSession);
  169. resolveBranches(data, branchMeasuresDtos);
  170. resolveQualityGates(data, dbSession);
  171. resolveUsers(data, dbSession);
  172. }
  173. data.setQualityProfiles(qualityProfileDataProvider.retrieveQualityProfilesData());
  174. setSecurityCustomConfigIfPresent(data);
  175. Optional<String> installationDateProperty = internalProperties.read(InternalProperties.INSTALLATION_DATE);
  176. installationDateProperty.ifPresent(s -> data.setInstallationDate(Long.valueOf(s)));
  177. Optional<String> installationVersionProperty = internalProperties.read(InternalProperties.INSTALLATION_VERSION);
  178. return data
  179. .setInstallationVersion(installationVersionProperty.orElse(null))
  180. .setInContainer(containerSupport.isRunningInContainer())
  181. .setManagedInstanceInformation(buildManagedInstanceInformation())
  182. .setCloudUsage(buildCloudUsage())
  183. .build();
  184. }
  185. private void resolveBranches(TelemetryData.Builder data, List<BranchMeasuresDto> branchMeasuresDtos) {
  186. var branches = branchMeasuresDtos.stream()
  187. .map(dto -> {
  188. var projectNcd = ncdByProject.getOrDefault(dto.getProjectUuid(), instanceNcd);
  189. var ncdId = ncdByBranch.getOrDefault(dto.getBranchUuid(), projectNcd).hashCode();
  190. return new TelemetryData.Branch(
  191. dto.getProjectUuid(), dto.getBranchUuid(), ncdId,
  192. dto.getGreenQualityGateCount(), dto.getAnalysisCount(), dto.getExcludeFromPurge());
  193. })
  194. .toList();
  195. data.setBranches(branches);
  196. }
  197. @Override
  198. public void reset() {
  199. this.newCodeDefinitions.clear();
  200. this.ncdByBranch.clear();
  201. this.ncdByProject.clear();
  202. this.instanceNcd = NewCodeDefinition.getInstanceDefault();
  203. this.defaultQualityProfileByLanguage.clear();
  204. this.qualityProfileByProjectAndLanguage.clear();
  205. }
  206. private void loadNewCodeDefinitions(DbSession dbSession, List<BranchMeasuresDto> branchMeasuresDtos) {
  207. var branchUuidByKey = branchMeasuresDtos.stream()
  208. .collect(Collectors.toMap(dto -> createBranchUniqueKey(dto.getProjectUuid(), dto.getBranchKey()), BranchMeasuresDto::getBranchUuid));
  209. List<NewCodePeriodDto> newCodePeriodDtos = dbClient.newCodePeriodDao().selectAll(dbSession);
  210. NewCodeDefinition ncd;
  211. boolean hasInstance = false;
  212. for (var dto : newCodePeriodDtos) {
  213. String projectUuid = dto.getProjectUuid();
  214. String branchUuid = dto.getBranchUuid();
  215. if (branchUuid == null && projectUuid == null) {
  216. ncd = new NewCodeDefinition(dto.getType().name(), dto.getValue(), "instance");
  217. this.instanceNcd = ncd;
  218. hasInstance = true;
  219. } else if (projectUuid != null) {
  220. var value = dto.getType() == REFERENCE_BRANCH ? branchUuidByKey.get(createBranchUniqueKey(projectUuid, dto.getValue())) : dto.getValue();
  221. if (branchUuid == null || isCommunityEdition()) {
  222. ncd = new NewCodeDefinition(dto.getType().name(), value, "project");
  223. this.ncdByProject.put(projectUuid, ncd);
  224. } else {
  225. ncd = new NewCodeDefinition(dto.getType().name(), value, "branch");
  226. this.ncdByBranch.put(branchUuid, ncd);
  227. }
  228. } else {
  229. throw new IllegalStateException(String.format("Error in loading telemetry data. New code definition for branch %s doesn't have a projectUuid", branchUuid));
  230. }
  231. this.newCodeDefinitions.add(ncd);
  232. }
  233. if (!hasInstance) {
  234. this.newCodeDefinitions.add(NewCodeDefinition.getInstanceDefault());
  235. }
  236. }
  237. private void loadQualityProfiles(DbSession dbSession) {
  238. dbClient.qualityProfileDao().selectAllDefaultProfiles(dbSession)
  239. .forEach(defaultQualityProfile -> this.defaultQualityProfileByLanguage.put(defaultQualityProfile.getLanguage(), defaultQualityProfile.getKee()));
  240. dbClient.qualityProfileDao().selectAllProjectAssociations(dbSession)
  241. .forEach(projectAssociation -> qualityProfileByProjectAndLanguage.put(
  242. new ProjectLanguageKey(projectAssociation.projectUuid(), projectAssociation.language()),
  243. projectAssociation.profileKey()));
  244. }
  245. private boolean isCommunityEdition() {
  246. var edition = editionProvider.get();
  247. return edition.isPresent() && edition.get() == COMMUNITY;
  248. }
  249. private static String createBranchUniqueKey(String projectUuid, @Nullable String branchKey) {
  250. return projectUuid + "-" + branchKey;
  251. }
  252. private void resolveUnanalyzedLanguageCode(TelemetryData.Builder data, DbSession dbSession) {
  253. long numberOfUnanalyzedCMeasures = dbClient.liveMeasureDao().countProjectsHavingMeasure(dbSession, UNANALYZED_C_KEY);
  254. long numberOfUnanalyzedCppMeasures = dbClient.liveMeasureDao().countProjectsHavingMeasure(dbSession, UNANALYZED_CPP_KEY);
  255. editionProvider.get()
  256. .filter(edition -> edition.equals(COMMUNITY))
  257. .ifPresent(edition -> {
  258. data.setHasUnanalyzedC(numberOfUnanalyzedCMeasures > 0);
  259. data.setHasUnanalyzedCpp(numberOfUnanalyzedCppMeasures > 0);
  260. });
  261. }
  262. private Long retrieveCurrentMessageSequenceNumber() {
  263. return internalProperties.read(I_PROP_MESSAGE_SEQUENCE).map(Long::parseLong).orElse(0L);
  264. }
  265. private void resolveProjectStatistics(TelemetryData.Builder data, DbSession dbSession, String defaultQualityGateUuid, List<ProjectDto> projects) {
  266. Map<String, String> scmByProject = getAnalysisPropertyByProject(dbSession, SONAR_ANALYSIS_DETECTEDSCM);
  267. Map<String, String> ciByProject = getAnalysisPropertyByProject(dbSession, SONAR_ANALYSIS_DETECTEDCI);
  268. Map<String, ProjectAlmKeyAndProject> almAndUrlAndMonorepoByProject = getAlmAndUrlByProject(dbSession);
  269. Map<String, PrBranchAnalyzedLanguageCountByProjectDto> prAndBranchCountByProject = dbClient.branchDao().countPrBranchAnalyzedLanguageByProjectUuid(dbSession)
  270. .stream().collect(toMap(PrBranchAnalyzedLanguageCountByProjectDto::getProjectUuid, Function.identity()));
  271. Map<String, String> qgatesByProject = getProjectQgatesMap(dbSession);
  272. Map<String, Map<String, Number>> metricsByProject = getProjectMetricsByMetricKeys(dbSession, TECHNICAL_DEBT_KEY, DEVELOPMENT_COST_KEY, SECURITY_HOTSPOTS_KEY,
  273. VULNERABILITIES_KEY,
  274. BUGS_KEY);
  275. Map<String, Long> securityReportExportedAtByProjectUuid = getSecurityReportExportedAtDateByProjectUuid(dbSession);
  276. List<TelemetryData.ProjectStatistics> projectStatistics = new ArrayList<>();
  277. for (ProjectDto project : projects) {
  278. String projectUuid = project.getUuid();
  279. Map<String, Number> metrics = metricsByProject.getOrDefault(projectUuid, Collections.emptyMap());
  280. Optional<PrBranchAnalyzedLanguageCountByProjectDto> counts = ofNullable(prAndBranchCountByProject.get(projectUuid));
  281. TelemetryData.ProjectStatistics stats = new TelemetryData.ProjectStatistics.Builder()
  282. .setProjectUuid(projectUuid)
  283. .setBranchCount(counts.map(PrBranchAnalyzedLanguageCountByProjectDto::getBranch).orElse(0L))
  284. .setPRCount(counts.map(PrBranchAnalyzedLanguageCountByProjectDto::getPullRequest).orElse(0L))
  285. .setQG(qgatesByProject.getOrDefault(projectUuid, defaultQualityGateUuid))
  286. .setScm(Optional.ofNullable(scmByProject.get(projectUuid)).orElse(UNDETECTED))
  287. .setCi(Optional.ofNullable(ciByProject.get(projectUuid)).orElse(UNDETECTED))
  288. .setDevops(resolveDevopsPlatform(almAndUrlAndMonorepoByProject, projectUuid))
  289. .setBugs(metrics.getOrDefault("bugs", null))
  290. .setDevelopmentCost(metrics.getOrDefault("development_cost", null))
  291. .setVulnerabilities(metrics.getOrDefault("vulnerabilities", null))
  292. .setSecurityHotspots(metrics.getOrDefault("security_hotspots", null))
  293. .setTechnicalDebt(metrics.getOrDefault("sqale_index", null))
  294. .setNcdId(ncdByProject.getOrDefault(projectUuid, instanceNcd).hashCode())
  295. .setExternalSecurityReportExportedAt(securityReportExportedAtByProjectUuid.get(projectUuid))
  296. .setCreationMethod(project.getCreationMethod())
  297. .setMonorepo(resolveMonorepo(almAndUrlAndMonorepoByProject, projectUuid))
  298. .build();
  299. projectStatistics.add(stats);
  300. }
  301. data.setProjectStatistics(projectStatistics);
  302. }
  303. private Map<String, Long> getSecurityReportExportedAtDateByProjectUuid(DbSession dbSession) {
  304. PropertyQuery propertyQuery = PropertyQuery.builder().setKey(EXTERNAL_SECURITY_REPORT_EXPORTED_AT).build();
  305. List<PropertyDto> properties = dbClient.propertiesDao().selectByQuery(propertyQuery, dbSession);
  306. return properties.stream()
  307. .collect(toMap(PropertyDto::getEntityUuid, propertyDto -> Long.parseLong(propertyDto.getValue())));
  308. }
  309. private static String resolveDevopsPlatform(Map<String, ProjectAlmKeyAndProject> almAndUrlByProject, String projectUuid) {
  310. if (almAndUrlByProject.containsKey(projectUuid)) {
  311. ProjectAlmKeyAndProject projectAlmKeyAndProject = almAndUrlByProject.get(projectUuid);
  312. return getAlmName(projectAlmKeyAndProject.getAlmId(), projectAlmKeyAndProject.getUrl());
  313. }
  314. return UNDETECTED;
  315. }
  316. private static Boolean resolveMonorepo(Map<String, ProjectAlmKeyAndProject> almAndUrlByProject, String projectUuid) {
  317. return Optional.ofNullable(almAndUrlByProject.get(projectUuid))
  318. .map(ProjectAlmKeyAndProject::getMonorepo)
  319. .orElse(false);
  320. }
  321. private void resolveProjects(TelemetryData.Builder data, DbSession dbSession) {
  322. Map<String, String> metricUuidMap = getNclocMetricUuidMap(dbSession);
  323. String nclocUuid = metricUuidMap.get(NCLOC_KEY);
  324. String nclocDistributionUuid = metricUuidMap.get(NCLOC_LANGUAGE_DISTRIBUTION_KEY);
  325. List<ProjectLocDistributionDto> branchesWithLargestNcloc = dbClient.liveMeasureDao().selectLargestBranchesLocDistribution(dbSession, nclocUuid, nclocDistributionUuid);
  326. List<String> branchUuids = branchesWithLargestNcloc.stream().map(ProjectLocDistributionDto::branchUuid).toList();
  327. Map<String, Long> latestSnapshotMap = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, branchUuids)
  328. .stream()
  329. .collect(toMap(SnapshotDto::getRootComponentUuid, SnapshotDto::getAnalysisDate));
  330. data.setProjects(buildProjectsList(branchesWithLargestNcloc, latestSnapshotMap));
  331. }
  332. private List<TelemetryData.Project> buildProjectsList(List<ProjectLocDistributionDto> branchesWithLargestNcloc, Map<String, Long> latestSnapshotMap) {
  333. return branchesWithLargestNcloc.stream()
  334. .flatMap(measure -> Arrays.stream(measure.locDistribution().split(";"))
  335. .map(languageAndLoc -> languageAndLoc.split("="))
  336. .map(languageAndLoc -> new TelemetryData.Project(
  337. measure.projectUuid(),
  338. latestSnapshotMap.get(measure.branchUuid()),
  339. languageAndLoc[0],
  340. getQualityProfile(measure.projectUuid(), languageAndLoc[0]),
  341. Long.parseLong(languageAndLoc[1]))))
  342. .toList();
  343. }
  344. private String getQualityProfile(String projectUuid, String language) {
  345. String qualityProfile = this.qualityProfileByProjectAndLanguage.get(new ProjectLanguageKey(projectUuid, language));
  346. if (qualityProfile != null) {
  347. return qualityProfile;
  348. }
  349. return this.defaultQualityProfileByLanguage.get(language);
  350. }
  351. private Map<String, String> getNclocMetricUuidMap(DbSession dbSession) {
  352. return dbClient.metricDao().selectByKeys(dbSession, asList(NCLOC_KEY, NCLOC_LANGUAGE_DISTRIBUTION_KEY))
  353. .stream()
  354. .collect(toMap(MetricDto::getKey, MetricDto::getUuid));
  355. }
  356. private void resolveQualityGates(TelemetryData.Builder data, DbSession dbSession) {
  357. List<TelemetryData.QualityGate> qualityGates = new ArrayList<>();
  358. Collection<QualityGateDto> qualityGateDtos = dbClient.qualityGateDao().selectAll(dbSession);
  359. Collection<QualityGateConditionDto> qualityGateConditions = dbClient.gateConditionDao().selectAll(dbSession);
  360. Map<String, MetricDto> metricsByUuid = getMetricsByUuid(dbSession, qualityGateConditions);
  361. Map<String, List<Condition>> conditionsMap = mapQualityGateConditions(qualityGateConditions, metricsByUuid);
  362. for (QualityGateDto qualityGateDto : qualityGateDtos) {
  363. String qualityGateUuid = qualityGateDto.getUuid();
  364. List<Condition> conditions = conditionsMap.getOrDefault(qualityGateUuid, Collections.emptyList());
  365. qualityGates.add(
  366. new TelemetryData.QualityGate(qualityGateDto.getUuid(), qualityGateCaycChecker.checkCaycCompliant(dbSession,
  367. qualityGateDto.getUuid()).toString(), conditions));
  368. }
  369. data.setQualityGates(qualityGates);
  370. }
  371. private static Map<String, List<Condition>> mapQualityGateConditions(Collection<QualityGateConditionDto> qualityGateConditions, Map<String, MetricDto> metricsByUuid) {
  372. Map<String, List<Condition>> conditionsMap = new HashMap<>();
  373. for (QualityGateConditionDto condition : qualityGateConditions) {
  374. String qualityGateUuid = condition.getQualityGateUuid();
  375. MetricDto metricDto = metricsByUuid.get(condition.getMetricUuid());
  376. String metricKey = metricDto != null ? metricDto.getKey() : "Unknown Metric";
  377. Condition telemetryCondition = new Condition(
  378. metricKey,
  379. fromDbValue(condition.getOperator()),
  380. condition.getErrorThreshold());
  381. conditionsMap
  382. .computeIfAbsent(qualityGateUuid, k -> new ArrayList<>())
  383. .add(telemetryCondition);
  384. }
  385. return conditionsMap;
  386. }
  387. private Map<String, MetricDto> getMetricsByUuid(DbSession dbSession, Collection<QualityGateConditionDto> conditions) {
  388. Set<String> metricUuids = conditions.stream().map(QualityGateConditionDto::getMetricUuid).collect(Collectors.toSet());
  389. return dbClient.metricDao().selectByUuids(dbSession, metricUuids).stream().filter(MetricDto::isEnabled).collect(Collectors.toMap(MetricDto::getUuid, Function.identity()));
  390. }
  391. private void resolveUsers(TelemetryData.Builder data, DbSession dbSession) {
  392. data.setUsers(dbClient.userDao().selectUsersForTelemetry(dbSession));
  393. }
  394. private void setSecurityCustomConfigIfPresent(TelemetryData.Builder data) {
  395. editionProvider.get()
  396. .filter(edition -> asList(ENTERPRISE, DATACENTER).contains(edition))
  397. .ifPresent(edition -> data.setCustomSecurityConfigs(getCustomerSecurityConfigurations()));
  398. }
  399. private Map<String, String> getAnalysisPropertyByProject(DbSession dbSession, String analysisPropertyKey) {
  400. return dbClient.analysisPropertiesDao()
  401. .selectAnalysisPropertyValueInLastAnalysisPerProject(dbSession, analysisPropertyKey)
  402. .stream()
  403. .collect(toMap(AnalysisPropertyValuePerProject::getProjectUuid, AnalysisPropertyValuePerProject::getPropertyValue));
  404. }
  405. private Map<String, ProjectAlmKeyAndProject> getAlmAndUrlByProject(DbSession dbSession) {
  406. List<ProjectAlmKeyAndProject> projectAlmKeyAndProjects = dbClient.projectAlmSettingDao().selectAlmTypeAndUrlByProject(dbSession);
  407. return projectAlmKeyAndProjects.stream().collect(toMap(ProjectAlmKeyAndProject::getProjectUuid, Function.identity()));
  408. }
  409. private static String getAlmName(String alm, String url) {
  410. if (checkIfCloudAlm(alm, ALM.GITHUB.getId(), url, "https://api.github.com")) {
  411. return "github_cloud";
  412. }
  413. if (checkIfCloudAlm(alm, ALM.GITLAB.getId(), url, "https://gitlab.com/api/v4")) {
  414. return "gitlab_cloud";
  415. }
  416. if (checkIfCloudAlm(alm, ALM.AZURE_DEVOPS.getId(), url, "https://dev.azure.com")) {
  417. return "azure_devops_cloud";
  418. }
  419. if (ALM.BITBUCKET_CLOUD.getId().equals(alm)) {
  420. return alm;
  421. }
  422. return alm + "_server";
  423. }
  424. private Map<String, String> getProjectQgatesMap(DbSession dbSession) {
  425. return dbClient.projectQgateAssociationDao().selectAll(dbSession)
  426. .stream()
  427. .collect(toMap(ProjectQgateAssociationDto::getUuid, p -> Optional.ofNullable(p.getGateUuid()).orElse("")));
  428. }
  429. private Map<String, Map<String, Number>> getProjectMetricsByMetricKeys(DbSession dbSession, String... metricKeys) {
  430. Map<String, String> metricNamesByUuid = dbClient.metricDao().selectByKeys(dbSession, asList(metricKeys))
  431. .stream()
  432. .collect(toMap(MetricDto::getUuid, MetricDto::getKey));
  433. // metrics can be empty for un-analyzed projects
  434. if (metricNamesByUuid.isEmpty()) {
  435. return Collections.emptyMap();
  436. }
  437. return dbClient.liveMeasureDao().selectForProjectMainBranchesByMetricUuids(dbSession, metricNamesByUuid.keySet())
  438. .stream()
  439. .collect(groupingBy(ProjectMainBranchLiveMeasureDto::getProjectUuid,
  440. toMap(lmDto -> metricNamesByUuid.get(lmDto.getMetricUuid()),
  441. lmDto -> Optional.ofNullable(lmDto.getValue()).orElseGet(() -> Double.valueOf(lmDto.getTextValue())),
  442. (oldValue, newValue) -> newValue, HashMap::new)));
  443. }
  444. private static boolean checkIfCloudAlm(String almRaw, String alm, String url, String cloudUrl) {
  445. return alm.equals(almRaw) && startsWithIgnoreCase(url, cloudUrl);
  446. }
  447. @Override
  448. public String loadServerId() {
  449. return server.getId();
  450. }
  451. private Set<String> getCustomerSecurityConfigurations() {
  452. return LANGUAGES_BY_SECURITY_JSON_PROPERTY_MAP.keySet().stream()
  453. .filter(this::isPropertyPresentInConfiguration)
  454. .map(LANGUAGES_BY_SECURITY_JSON_PROPERTY_MAP::get)
  455. .collect(Collectors.toSet());
  456. }
  457. private boolean isPropertyPresentInConfiguration(String property) {
  458. return configuration.get(property).isPresent();
  459. }
  460. private TelemetryData.ManagedInstanceInformation buildManagedInstanceInformation() {
  461. String provider = managedInstanceService.isInstanceExternallyManaged() ? managedInstanceService.getProviderName() : null;
  462. return new TelemetryData.ManagedInstanceInformation(managedInstanceService.isInstanceExternallyManaged(), provider);
  463. }
  464. private TelemetryData.CloudUsage buildCloudUsage() {
  465. return cloudUsageDataProvider.getCloudUsage();
  466. }
  467. private record ProjectLanguageKey(String projectKey, String language) {
  468. }
  469. }