QProfileDto profile3 = newQualityProfileDto();
assertThat(underTest.selectAllProjectAssociations(dbSession))
- .extracting("projectUuid", "projectKey", "projectName", "profileKey")
+ .extracting("projectUuid", "profileKey", "language")
.containsExactlyInAnyOrder(
- tuple(project1.getUuid(), project1.getKey(), project1.getName(), profile1.getKee()),
- tuple(project2.getUuid(), project2.getKey(), project2.getName(), profile2.getKee())
- );
+ tuple(project1.getUuid(), profile1.getKee(), profile1.getLanguage()),
+ tuple(project2.getUuid(), profile2.getKee(), profile2.getLanguage()));
}
@Test
.extracting("kee", "name", "language")
.containsExactlyInAnyOrder(
tuple(defaultProfile1.getKee(), defaultProfile1.getName(), defaultProfile1.getLanguage()),
- tuple(defaultProfile2.getKee(), defaultProfile2.getName(), defaultProfile2.getLanguage())
- );
+ tuple(defaultProfile2.getKee(), defaultProfile2.getName(), defaultProfile2.getLanguage()));
}
@Test
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.qualityprofile;
+
+public record ProjectQProfileLanguageAssociationDto(String projectUuid, String profileKey, String language) {
+}
return mapper(dbSession).selectProjectAssociations(profile.getKee(), nameQuery);
}
- public List<ProjectQprofileAssociationDto> selectAllProjectAssociations(DbSession dbSession) {
+ public List<ProjectQProfileLanguageAssociationDto> selectAllProjectAssociations(DbSession dbSession) {
return mapper(dbSession).selectAllProjectAssociations();
}
public Collection<String> selectUuidsOfCustomRulesProfiles(DbSession dbSession, String language, String name) {
@Param("oldProfileUuid") String oldProfileUuid);
void deleteProjectProfileAssociation(@Param("projectUuid") String projectUuid, @Param("profileUuid") String profileUuid);
+
void deleteProjectAssociationByProfileUuids(@Param("profileUuids") Collection<String> profileUuids);
List<ProjectQprofileAssociationDto> selectSelectedProjects(
@Param("profileUuid") String profileUuid,
@Param("nameOrKeyQuery") String nameOrKeyQuery);
- List<ProjectQprofileAssociationDto> selectAllProjectAssociations();
+ List<ProjectQProfileLanguageAssociationDto> selectAllProjectAssociations();
List<String> selectUuidsOfCustomRuleProfiles(@Param("language") String language, @Param("name") String name);
ORDER BY pj.name ASC
</select>
- <select id="selectAllProjectAssociations" resultType="org.sonar.db.qualityprofile.ProjectQprofileAssociationDto">
- SELECT
- pj.uuid as projectUuid,
- pj.kee as projectKey,
- pj.name as projectName,
- pp.profile_key as profileKey
- FROM projects pj
- INNER JOIN project_qprofiles pp ON pp.project_uuid = pj.uuid
- WHERE
- pj.qualifier='TRK'
+ <select id="selectAllProjectAssociations" resultType="org.sonar.db.qualityprofile.ProjectQProfileLanguageAssociationDto">
+ SELECT
+ pj.uuid as projectUuid,
+ pp.profile_key as profileKey,
+ rp.language as language
+ FROM projects pj
+ INNER JOIN project_qprofiles pp ON pp.project_uuid = pj.uuid
+ INNER JOIN org_qprofiles oq ON pp.profile_key = oq.uuid
+ INNER JOIN rules_profiles rp on oq.rules_profile_uuid = rp.uuid
+ WHERE pj.qualifier = 'TRK'
</select>
<select id="selectUuidsOfCustomRuleProfiles" parameterType="map" resultType="string">
record Branch(String projectUuid, String branchUuid, int ncdId, int greenQualityGateCount, int analysisCount, boolean excludeFromPurge) {
}
- record Project(String projectUuid, Long lastAnalysis, String language, Long loc) {
+ record Project(String projectUuid, Long lastAnalysis, String language, String qualityProfile, Long loc) {
}
record QualityGate(String uuid, String caycStatus) {
}
json.prop(LANGUAGE_PROPERTY, project.language());
json.prop("loc", project.loc());
+ json.prop("qualityProfile", project.qualityProfile());
json.endObject();
});
json.endArray();
"projectUuid": "uuid-0",
"lastAnalysis": "1970-01-01T00:00:00+0000",
"language": "lang-0",
+ "qualityProfile" : "qprofile-0",
"loc": 2
},
{
"projectUuid": "uuid-1",
"lastAnalysis": "1970-01-01T00:00:00+0000",
"language": "lang-1",
+ "qualityProfile" : "qprofile-1",
"loc": 4
},
{
"projectUuid": "uuid-2",
"lastAnalysis": "1970-01-01T00:00:00+0000",
"language": "lang-2",
+ "qualityProfile" : "qprofile-2",
"loc": 6
}
]
}
private static List<TelemetryData.Project> attachProjects() {
- return IntStream.range(0, 3).mapToObj(i -> new TelemetryData.Project("uuid-" + i, 1L, "lang-" + i, (i + 1L) * 2L)).toList();
+ return IntStream.range(0, 3).mapToObj(i -> new TelemetryData.Project("uuid-" + i, 1L, "lang-" + i, "qprofile-" + i, (i + 1L) * 2L)).toList();
}
private static List<TelemetryData.ProjectStatistics> attachProjectStatsWithMetrics() {
private final Set<NewCodeDefinition> newCodeDefinitions = new HashSet<>();
private final Map<String, NewCodeDefinition> ncdByProject = new HashMap<>();
private final Map<String, NewCodeDefinition> ncdByBranch = new HashMap<>();
+ private final Map<String, String> defaultQualityProfileByLanguage = new HashMap<>();
+ private final Map<ProjectLanguageKey, String> qualityProfileByProjectAndLanguage = new HashMap<>();
private NewCodeDefinition instanceNcd = NewCodeDefinition.getInstanceDefault();
@Inject
try (DbSession dbSession = dbClient.openSession(false)) {
var branchMeasuresDtos = dbClient.branchDao().selectBranchMeasuresWithCaycMetric(dbSession);
loadNewCodeDefinitions(dbSession, branchMeasuresDtos);
+ loadQualityProfiles(dbSession);
data.setDatabase(loadDatabaseMetadata(dbSession));
data.setNcdId(instanceNcd.hashCode());
this.ncdByBranch.clear();
this.ncdByProject.clear();
this.instanceNcd = NewCodeDefinition.getInstanceDefault();
+ this.defaultQualityProfileByLanguage.clear();
+ this.qualityProfileByProjectAndLanguage.clear();
}
private void loadNewCodeDefinitions(DbSession dbSession, List<BranchMeasuresDto> branchMeasuresDtos) {
}
}
+ private void loadQualityProfiles(DbSession dbSession) {
+ dbClient.qualityProfileDao().selectAllDefaultProfiles(dbSession)
+ .forEach(defaultQualityProfile -> this.defaultQualityProfileByLanguage.put(defaultQualityProfile.getLanguage(), defaultQualityProfile.getKee()));
+
+ dbClient.qualityProfileDao().selectAllProjectAssociations(dbSession)
+ .forEach(projectAssociation -> qualityProfileByProjectAndLanguage.put(
+ new ProjectLanguageKey(projectAssociation.projectUuid(), projectAssociation.language()),
+ projectAssociation.profileKey()));
+ }
+
private boolean isCommunityEdition() {
var edition = editionProvider.get();
return edition.isPresent() && edition.get() == COMMUNITY;
Map<String, String> scmByProject = getAnalysisPropertyByProject(dbSession, SONAR_ANALYSIS_DETECTEDSCM);
Map<String, String> ciByProject = getAnalysisPropertyByProject(dbSession, SONAR_ANALYSIS_DETECTEDCI);
Map<String, ProjectAlmKeyAndProject> almAndUrlByProject = getAlmAndUrlByProject(dbSession);
- Map<String, PrBranchAnalyzedLanguageCountByProjectDto> prAndBranchCountByProject =
- dbClient.branchDao().countPrBranchAnalyzedLanguageByProjectUuid(dbSession)
- .stream().collect(toMap(PrBranchAnalyzedLanguageCountByProjectDto::getProjectUuid, Function.identity()));
+ Map<String, PrBranchAnalyzedLanguageCountByProjectDto> prAndBranchCountByProject = dbClient.branchDao().countPrBranchAnalyzedLanguageByProjectUuid(dbSession)
+ .stream().collect(toMap(PrBranchAnalyzedLanguageCountByProjectDto::getProjectUuid, Function.identity()));
Map<String, String> qgatesByProject = getProjectQgatesMap(dbSession);
- Map<String, Map<String, Number>> metricsByProject =
- getProjectMetricsByMetricKeys(dbSession, TECHNICAL_DEBT_KEY, DEVELOPMENT_COST_KEY, SECURITY_HOTSPOTS_KEY, VULNERABILITIES_KEY,
- BUGS_KEY);
+ Map<String, Map<String, Number>> metricsByProject = getProjectMetricsByMetricKeys(dbSession, TECHNICAL_DEBT_KEY, DEVELOPMENT_COST_KEY, SECURITY_HOTSPOTS_KEY,
+ VULNERABILITIES_KEY,
+ BUGS_KEY);
Map<String, Long> securityReportExportedAtByProjectUuid = getSecurityReportExportedAtDateByProjectUuid(dbSession);
List<TelemetryData.ProjectStatistics> projectStatistics = new ArrayList<>();
data.setProjects(buildProjectsList(branchesWithLargestNcloc, latestSnapshotMap));
}
- private static List<TelemetryData.Project> buildProjectsList(List<ProjectLocDistributionDto> branchesWithLargestNcloc,
+ private List<TelemetryData.Project> buildProjectsList(List<ProjectLocDistributionDto> branchesWithLargestNcloc,
Map<String, Long> latestSnapshotMap) {
return branchesWithLargestNcloc.stream()
.flatMap(measure -> Arrays.stream(measure.locDistribution().split(";"))
measure.projectUuid(),
latestSnapshotMap.get(measure.branchUuid()),
languageAndLoc[0],
- Long.parseLong(languageAndLoc[1])
- ))
- ).toList();
+ getQualityProfile(measure.projectUuid(), languageAndLoc[0]),
+ Long.parseLong(languageAndLoc[1]))))
+ .toList();
+ }
+
+ private String getQualityProfile(String projectUuid, String language) {
+ String qualityProfile = this.qualityProfileByProjectAndLanguage.get(new ProjectLanguageKey(projectUuid, language));
+ if (qualityProfile != null) {
+ return qualityProfile;
+ }
+ return this.defaultQualityProfileByLanguage.get(language);
}
private Map<String, String> getNclocMetricUuidMap(DbSession dbSession) {
for (QualityGateDto qualityGateDto : qualityGateDtos) {
qualityGates.add(
new TelemetryData.QualityGate(qualityGateDto.getUuid(), qualityGateCaycChecker.checkCaycCompliant(dbSession,
- qualityGateDto.getUuid()).toString())
- );
+ qualityGateDto.getUuid()).toString()));
}
data.setQualityGates(qualityGates);
}
-
-
private void resolveUsers(TelemetryData.Builder data, DbSession dbSession) {
data.setUsers(dbClient.userDao().selectUsersForTelemetry(dbSession));
}
private TelemetryData.CloudUsage buildCloudUsage() {
return cloudUsageDataProvider.getCloudUsage();
}
+
+ private record ProjectLanguageKey(String projectKey, String language) {
+ }
}
QualityGateDto qualityGate2 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG2"));
//quality profiles
+ QProfileDto javaQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("java"));
+ QProfileDto kotlinQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("kotlin"));
+ QProfileDto jsQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("js"));
+ db.qualityProfiles().associateWithProject(projectData1.getProjectDto(), javaQP, kotlinQP, jsQP);
+ db.qualityProfiles().associateWithProject(projectData2.getProjectDto(), javaQP, jsQP);
+
QProfileDto qualityProfile1 = db.qualityProfiles().insert(qp -> qp.setIsBuiltIn(true));
QProfileDto qualityProfile2 = db.qualityProfiles().insert();
db.qualityProfiles().setAsDefault(qualityProfile1, qualityProfile2);
assertThat(data.getQualityProfiles())
.extracting(TelemetryData.QualityProfile::uuid, TelemetryData.QualityProfile::isBuiltIn)
- .containsExactlyInAnyOrder(tuple(qualityProfile1.getKee(), qualityProfile1.isBuiltIn()), tuple(qualityProfile2.getKee(), qualityProfile2.isBuiltIn()));
+ .containsExactlyInAnyOrder(
+ tuple(qualityProfile1.getKee(), qualityProfile1.isBuiltIn()),
+ tuple(qualityProfile2.getKee(), qualityProfile2.isBuiltIn()),
+ tuple(jsQP.getKee(), jsQP.isBuiltIn()),
+ tuple(javaQP.getKee(), javaQP.isBuiltIn()),
+ tuple(kotlinQP.getKee(), kotlinQP.isBuiltIn())
+ );
}
MetricDto nclocDistrib = db.measures().insertMetric(m -> m.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY));
ProjectData projectData = db.components().insertPublicProject();
+
+ QProfileDto javaQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("java"));
+ QProfileDto kotlinQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("kotlin"));
+ QProfileDto jsQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("js"));
+ db.qualityProfiles().associateWithProject(projectData.getProjectDto(), javaQP, kotlinQP, jsQP);
+
ComponentDto mainBranch = projectData.getMainBranchComponent();
db.measures().insertLiveMeasure(mainBranch, lines, m -> m.setValue(110d));
db.measures().insertLiveMeasure(mainBranch, ncloc, m -> m.setValue(110d));
tuple(2L, 0L, "undetected", "undetected"));
}
+ @Test
+ public void load_shouldProvideQualityProfileInProjectSection() {
+ server.setId("AU-TpxcB-iU5OvuD2FL7").setVersion("7.5.4");
+ MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(NCLOC_KEY));
+ MetricDto nclocDistrib = db.measures().insertMetric(m -> m.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY));
+
+ ProjectData projectData = db.components().insertPublicProject();
+
+ // default quality profile
+ QProfileDto javaQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("java"));
+ QProfileDto kotlinQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("kotlin"));
+ db.qualityProfiles().setAsDefault(javaQP,kotlinQP);
+ // selected quality profile
+ QProfileDto jsQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("js"));
+ db.qualityProfiles().associateWithProject(projectData.getProjectDto(), jsQP);
+
+
+ ComponentDto mainBranch = projectData.getMainBranchComponent();
+ db.measures().insertLiveMeasure(mainBranch, ncloc, m -> m.setValue(110d));
+ db.measures().insertLiveMeasure(mainBranch, nclocDistrib, m -> m.setValue(null).setData("java=70;js=30;kotlin=10"));
+
+ ComponentDto branch = db.components().insertProjectBranch(mainBranch, b -> b.setBranchType(BRANCH));
+ db.measures().insertLiveMeasure(branch, ncloc, m -> m.setValue(180d));
+ db.measures().insertLiveMeasure(branch, nclocDistrib, m -> m.setValue(null).setData("java=100;js=50;kotlin=30"));
+
+ SnapshotDto project1Analysis = db.components().insertSnapshot(mainBranch, t -> t.setLast(true));
+ SnapshotDto project2Analysis = db.components().insertSnapshot(branch, t -> t.setLast(true));
+ db.measures().insertMeasure(mainBranch, project1Analysis, nclocDistrib, m -> m.setData("java=70;js=30;kotlin=10"));
+ db.measures().insertMeasure(branch, project2Analysis, nclocDistrib, m -> m.setData("java=100;js=50;kotlin=30"));
+
+ TelemetryData data = communityUnderTest.load();
+
+ assertThat(data.getProjects()).extracting(TelemetryData.Project::projectUuid, TelemetryData.Project::language, TelemetryData.Project::qualityProfile)
+ .containsExactlyInAnyOrder(
+ tuple(projectData.projectUuid(), "java", javaQP.getKee()),
+ tuple(projectData.projectUuid(), "js", jsQP.getKee()),
+ tuple(projectData.projectUuid(), "kotlin", kotlinQP.getKee()));
+ }
+
@Test
public void test_ncd_on_community_edition() {
server.setId("AU-TpxcB-iU5OvuD2FL7").setVersion("7.5.4");