@@ -44,6 +44,7 @@ class ProjectAlmSettingDaoIT { | |||
private static final long A_DATE_LATER = 1_700_000_000_000L; | |||
private static final String A_UUID = "SOME_UUID"; | |||
private static final String ANOTHER_UUID = "SOME_UUID2"; | |||
private final TestSystem2 system2 = new TestSystem2().setNow(A_DATE); | |||
@RegisterExtension | |||
private final DbTester db = DbTester.create(system2); | |||
@@ -117,8 +118,8 @@ class ProjectAlmSettingDaoIT { | |||
AlmSettingDto gitlabSetting = db.almSettings().insertGitlabAlmSetting(); | |||
ProjectAlmSettingDto gitlabProject = createAlmProject(gitlabSetting); | |||
List<ProjectAlmSettingDto> projectAlmSettingDtos = | |||
underTest.selectByProjectUuidsAndAlm(dbSession, Set.of(githubProject.getProjectUuid(), gitlabProject.getProjectUuid()), ALM.GITHUB); | |||
List<ProjectAlmSettingDto> projectAlmSettingDtos = underTest.selectByProjectUuidsAndAlm(dbSession, Set.of(githubProject.getProjectUuid(), gitlabProject.getProjectUuid()), | |||
ALM.GITHUB); | |||
assertThat(projectAlmSettingDtos) | |||
.usingRecursiveFieldByFieldElementComparator() | |||
@@ -171,15 +172,30 @@ class ProjectAlmSettingDaoIT { | |||
} | |||
@Test | |||
void select_alm_type_and_url_by_project() { | |||
when(uuidFactory.create()).thenReturn(A_UUID); | |||
void selectAlmTypeAndUrlByProject_returnsCorrectValues() { | |||
when(uuidFactory.create()) | |||
.thenReturn(A_UUID) | |||
.thenReturn(ANOTHER_UUID); | |||
AlmSettingDto almSettingsDto = db.almSettings().insertGitHubAlmSetting(); | |||
ProjectDto project = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectAlmSettingDto githubProjectAlmSettingDto = newGithubProjectAlmSettingDto(almSettingsDto, project); | |||
underTest.insertOrUpdate(dbSession, githubProjectAlmSettingDto, almSettingsDto.getKey(), project.getName(), project.getKey()); | |||
ProjectDto project1 = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto project2 = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectAlmSettingDto githubProjectAlmSettingDto1 = newGithubProjectAlmSettingDto(almSettingsDto, project1, false); | |||
ProjectAlmSettingDto githubProjectAlmSettingDto2 = newGithubProjectAlmSettingDto(almSettingsDto, project2, true); | |||
underTest.insertOrUpdate(dbSession, githubProjectAlmSettingDto1, almSettingsDto.getKey(), project1.getName(), project1.getKey()); | |||
underTest.insertOrUpdate(dbSession, githubProjectAlmSettingDto2, almSettingsDto.getKey(), project2.getName(), project2.getKey()); | |||
assertThat(underTest.selectAlmTypeAndUrlByProject(dbSession)) | |||
.extracting(ProjectAlmKeyAndProject::getProjectUuid, ProjectAlmKeyAndProject::getAlmId, ProjectAlmKeyAndProject::getUrl) | |||
.containsExactly(tuple(project.getUuid(), almSettingsDto.getAlm().getId(), almSettingsDto.getUrl())); | |||
.extracting( | |||
ProjectAlmKeyAndProject::getProjectUuid, | |||
ProjectAlmKeyAndProject::getAlmId, | |||
ProjectAlmKeyAndProject::getUrl, | |||
ProjectAlmKeyAndProject::getMonorepo | |||
).containsExactlyInAnyOrder( | |||
tuple(project1.getUuid(), almSettingsDto.getAlm().getId(), almSettingsDto.getUrl(), false), | |||
tuple(project2.getUuid(), almSettingsDto.getAlm().getId(), almSettingsDto.getUrl(), true)); | |||
} | |||
@Test |
@@ -24,6 +24,7 @@ public class ProjectAlmKeyAndProject { | |||
private String projectUuid; | |||
private String almId; | |||
private String url; | |||
private Boolean monorepo; | |||
public ProjectAlmKeyAndProject() { | |||
// keep empty | |||
@@ -55,4 +56,12 @@ public class ProjectAlmKeyAndProject { | |||
this.url = url; | |||
return this; | |||
} | |||
public Boolean getMonorepo() { | |||
return monorepo; | |||
} | |||
public ProjectAlmKeyAndProject setMonorepo(Boolean monorepo) { | |||
this.monorepo = monorepo; | |||
return this; | |||
} | |||
} |
@@ -19,7 +19,8 @@ | |||
select | |||
pas.project_uuid as "projectUuid", | |||
alm_settings.alm_id as "almId", | |||
alm_settings.url as "url" | |||
alm_settings.url as "url", | |||
pas.monorepo as "monorepo" | |||
from | |||
project_alm_settings pas | |||
inner join |
@@ -88,7 +88,11 @@ public class AlmSettingsDbTester { | |||
} | |||
public ProjectAlmSettingDto insertGitlabProjectAlmSetting(AlmSettingDto gitlabAlmSetting, ProjectDto project) { | |||
return insertProjectAlmSetting(newGitlabProjectAlmSettingDto(gitlabAlmSetting, project), gitlabAlmSetting.getKey(), | |||
return insertGitlabProjectAlmSetting(gitlabAlmSetting, project, false); | |||
} | |||
public ProjectAlmSettingDto insertGitlabProjectAlmSetting(AlmSettingDto gitlabAlmSetting, ProjectDto project, boolean monorepo) { | |||
return insertProjectAlmSetting(newGitlabProjectAlmSettingDto(gitlabAlmSetting, project, monorepo), gitlabAlmSetting.getKey(), | |||
project.getName(), project.getKey()); | |||
} | |||
@@ -78,19 +78,27 @@ public class AlmSettingsTesting { | |||
} | |||
public static ProjectAlmSettingDto newGithubProjectAlmSettingDto(AlmSettingDto githubAlmSetting, ProjectDto project) { | |||
return newGithubProjectAlmSettingDto(githubAlmSetting, project, false); | |||
} | |||
public static ProjectAlmSettingDto newGithubProjectAlmSettingDto(AlmSettingDto githubAlmSetting, ProjectDto project, boolean monorepo) { | |||
return new ProjectAlmSettingDto() | |||
.setAlmSettingUuid(githubAlmSetting.getUuid()) | |||
.setProjectUuid(project.getUuid()) | |||
.setAlmRepo(randomAlphanumeric(256)) | |||
.setSummaryCommentEnabled(true) | |||
.setMonorepo(false); | |||
.setMonorepo(monorepo); | |||
} | |||
public static ProjectAlmSettingDto newGitlabProjectAlmSettingDto(AlmSettingDto gitlabAlmSetting, ProjectDto project) { | |||
return newGitlabProjectAlmSettingDto(gitlabAlmSetting, project, false); | |||
} | |||
public static ProjectAlmSettingDto newGitlabProjectAlmSettingDto(AlmSettingDto gitlabAlmSetting, ProjectDto project, boolean monorepo) { | |||
return new ProjectAlmSettingDto() | |||
.setAlmSettingUuid(gitlabAlmSetting.getUuid()) | |||
.setProjectUuid(project.getUuid()) | |||
.setMonorepo(false); | |||
.setMonorepo(monorepo); | |||
} | |||
static ProjectAlmSettingDto newAzureProjectAlmSettingDto(AlmSettingDto azureAlmSetting, ProjectDto project) { |
@@ -27,7 +27,6 @@ import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import javax.annotation.Nullable; | |||
import org.sonar.core.platform.EditionProvider; | |||
import org.sonar.core.platform.EditionProvider.Edition; | |||
import org.sonar.db.project.CreationMethod; | |||
import org.sonar.db.user.UserTelemetryDto; | |||
@@ -42,7 +41,7 @@ public class TelemetryData { | |||
private final Long messageSequenceNumber; | |||
private final Map<String, String> plugins; | |||
private final Database database; | |||
private final EditionProvider.Edition edition; | |||
private final Edition edition; | |||
private final String defaultQualityGate; | |||
private final String sonarWayQualityGate; | |||
private final Long installationDate; | |||
@@ -109,7 +108,7 @@ public class TelemetryData { | |||
return database; | |||
} | |||
public Optional<EditionProvider.Edition> getEdition() { | |||
public Optional<Edition> getEdition() { | |||
return Optional.ofNullable(edition); | |||
} | |||
@@ -325,7 +324,6 @@ public class TelemetryData { | |||
return this; | |||
} | |||
Builder setQualityProfiles(List<QualityProfile> qualityProfiles) { | |||
this.qualityProfiles = qualityProfiles; | |||
return this; | |||
@@ -378,18 +376,17 @@ public class TelemetryData { | |||
} | |||
public record QualityProfile(String uuid, @Nullable String parentUuid, String language, boolean isDefault, | |||
boolean isBuiltIn, | |||
@Nullable Boolean builtInParent, @Nullable Integer rulesOverriddenCount, | |||
@Nullable Integer rulesActivatedCount, @Nullable Integer rulesDeactivatedCount | |||
) { | |||
boolean isBuiltIn, | |||
@Nullable Boolean builtInParent, @Nullable Integer rulesOverriddenCount, | |||
@Nullable Integer rulesActivatedCount, @Nullable Integer rulesDeactivatedCount) { | |||
} | |||
record ManagedInstanceInformation(boolean isManaged, @Nullable String provider) { | |||
} | |||
record CloudUsage(boolean kubernetes, @Nullable String kubernetesVersion, @Nullable String kubernetesPlatform, | |||
@Nullable String kubernetesProvider, | |||
@Nullable String officialHelmChart, @Nullable String containerRuntime, boolean officialImage) { | |||
@Nullable String kubernetesProvider, | |||
@Nullable String officialHelmChart, @Nullable String containerRuntime, boolean officialImage) { | |||
} | |||
public static class ProjectStatistics { | |||
@@ -407,8 +404,8 @@ public class TelemetryData { | |||
private final Long developmentCost; | |||
private final int ncdId; | |||
private final Long externalSecurityReportExportedAt; | |||
private final CreationMethod creationMethod; | |||
private final Boolean monorepo; | |||
ProjectStatistics(Builder builder) { | |||
this.projectUuid = builder.projectUuid; | |||
@@ -426,6 +423,7 @@ public class TelemetryData { | |||
this.ncdId = builder.ncdId; | |||
this.externalSecurityReportExportedAt = builder.externalSecurityReportExportedAt; | |||
this.creationMethod = builder.creationMethod; | |||
this.monorepo = builder.monorepo; | |||
} | |||
public int getNcdId() { | |||
@@ -488,6 +486,10 @@ public class TelemetryData { | |||
return creationMethod; | |||
} | |||
public Boolean isMonorepo() { | |||
return monorepo; | |||
} | |||
static class Builder { | |||
private String projectUuid; | |||
private Long branchCount; | |||
@@ -504,6 +506,7 @@ public class TelemetryData { | |||
private int ncdId; | |||
private Long externalSecurityReportExportedAt; | |||
private CreationMethod creationMethod; | |||
private Boolean monorepo; | |||
public Builder setProjectUuid(String projectUuid) { | |||
this.projectUuid = projectUuid; | |||
@@ -580,6 +583,11 @@ public class TelemetryData { | |||
return this; | |||
} | |||
public Builder setMonorepo(Boolean monorepo) { | |||
this.monorepo = monorepo; | |||
return this; | |||
} | |||
public ProjectStatistics build() { | |||
return new ProjectStatistics(this); | |||
} |
@@ -203,6 +203,7 @@ public class TelemetryDataJsonWriter { | |||
json.prop("devopsPlatform", project.getDevopsPlatform()); | |||
json.prop(NCD_ID, project.getNcdId()); | |||
json.prop("project_creation_method", project.getCreationMethod().name()); | |||
json.prop("monorepo", project.isMonorepo()); | |||
project.getBugs().ifPresent(bugs -> json.prop("bugs", bugs)); | |||
project.getVulnerabilities().ifPresent(vulnerabilities -> json.prop("vulnerabilities", vulnerabilities)); | |||
project.getSecurityHotspots().ifPresent(securityHotspots -> json.prop("securityHotspots", securityHotspots)); |
@@ -463,7 +463,8 @@ public class TelemetryDataJsonWriterTest { | |||
"developmentCost": 30, | |||
"ncdId": 12345, | |||
"externalSecurityReportExportedAt": 1500000, | |||
"project_creation_method": "LOCAL_API" | |||
"project_creation_method": "LOCAL_API", | |||
"monorepo": true | |||
}, | |||
{ | |||
"projectUuid": "uuid-1", | |||
@@ -480,7 +481,8 @@ public class TelemetryDataJsonWriterTest { | |||
"developmentCost": 60, | |||
"ncdId": 12345, | |||
"externalSecurityReportExportedAt": 1500001, | |||
"project_creation_method": "LOCAL_API" | |||
"project_creation_method": "LOCAL_API", | |||
"monorepo": false | |||
}, | |||
{ | |||
"projectUuid": "uuid-2", | |||
@@ -497,7 +499,8 @@ public class TelemetryDataJsonWriterTest { | |||
"developmentCost": 90, | |||
"ncdId": 12345, | |||
"externalSecurityReportExportedAt": 1500002, | |||
"project_creation_method": "LOCAL_API" | |||
"project_creation_method": "LOCAL_API", | |||
"monorepo": true | |||
} | |||
] | |||
} | |||
@@ -730,7 +733,8 @@ public class TelemetryDataJsonWriterTest { | |||
.setScm("scm-" + i) | |||
.setDevops("devops-" + i) | |||
.setNcdId(NCD_ID) | |||
.setCreationMethod(CreationMethod.LOCAL_API); | |||
.setCreationMethod(CreationMethod.LOCAL_API) | |||
.setMonorepo(false); | |||
} | |||
private static TelemetryData.ProjectStatistics.Builder getProjectStatisticsWithMetricBuilder(int i) { | |||
@@ -741,7 +745,8 @@ public class TelemetryDataJsonWriterTest { | |||
.setDevelopmentCost((i + 1L) * 30d) | |||
.setTechnicalDebt((i + 1L) * 60d) | |||
.setExternalSecurityReportExportedAt(1_500_000L + i) | |||
.setCreationMethod(CreationMethod.LOCAL_API); | |||
.setCreationMethod(CreationMethod.LOCAL_API) | |||
.setMonorepo(i % 2 == 0); | |||
} | |||
private List<TelemetryData.QualityGate> attachQualityGates() { |
@@ -215,7 +215,7 @@ public class TelemetryDataLoaderImplIT { | |||
AlmSettingDto almSettingDto = db.almSettings().insertAzureAlmSetting(a -> a.setUrl("https://dev.azure.com")); | |||
AlmSettingDto gitHubAlmSetting = db.almSettings().insertGitHubAlmSetting(a -> a.setUrl("https://api.github.com")); | |||
db.almSettings().insertAzureProjectAlmSetting(almSettingDto, projectData1.getProjectDto()); | |||
db.almSettings().insertGitlabProjectAlmSetting(gitHubAlmSetting, projectData2.getProjectDto()); | |||
db.almSettings().insertGitlabProjectAlmSetting(gitHubAlmSetting, projectData2.getProjectDto(), true); | |||
// quality gates | |||
QualityGateDto qualityGate1 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG1").setBuiltIn(true)); | |||
@@ -238,8 +238,8 @@ public class TelemetryDataLoaderImplIT { | |||
// link one project to a non-default QG | |||
db.qualityGates().associateProjectToQualityGate(db.components().getProjectDtoByMainBranch(mainBranch1), qualityGate1); | |||
var ncd1 = db.newCodePeriods().insert(projectData1.projectUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30"); | |||
var ncd2 = db.newCodePeriods().insert(projectData1.projectUuid(), branch2.branchUuid(), NewCodePeriodType.REFERENCE_BRANCH, "reference"); | |||
db.newCodePeriods().insert(projectData1.projectUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30"); | |||
db.newCodePeriods().insert(projectData1.projectUuid(), branch2.branchUuid(), NewCodePeriodType.REFERENCE_BRANCH, "reference"); | |||
var instanceNcdId = NewCodeDefinition.getInstanceDefault().hashCode(); | |||
var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode(); | |||
@@ -274,15 +274,25 @@ public class TelemetryDataLoaderImplIT { | |||
tuple(projectData2.projectUuid(), "java", 180L, analysisDate), | |||
tuple(projectData2.projectUuid(), "js", 20L, analysisDate)); | |||
assertThat(data.getProjectStatistics()) | |||
.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) | |||
.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, | |||
ProjectStatistics::isMonorepo) | |||
.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), | |||
projectNcdId, false), | |||
tuple(1L, 0L, builtInDefaultQualityGate.getUuid(), "scm-2", "ci-2", "github_cloud", Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), | |||
Optional.empty(), instanceNcdId)); | |||
Optional.empty(), instanceNcdId, true)); | |||
assertThat(data.getBranches()) | |||
.extracting(Branch::branchUuid, Branch::ncdId) | |||
@@ -711,8 +721,6 @@ public class TelemetryDataLoaderImplIT { | |||
.setCreatedAt(1L)); | |||
} | |||
@DataProvider | |||
public static Set<String> getScimFeatureStatues() { | |||
HashSet<String> result = new HashSet<>(); |
@@ -222,7 +222,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { | |||
this.qualityProfileByProjectAndLanguage.clear(); | |||
} | |||
private void loadNewCodeDefinitions(DbSession dbSession, List<BranchMeasuresDto> branchMeasuresDtos) { | |||
private void loadNewCodeDefinitions(DbSession dbSession, List<BranchMeasuresDto> branchMeasuresDtos) { | |||
var branchUuidByKey = branchMeasuresDtos.stream() | |||
.collect(Collectors.toMap(dto -> createBranchUniqueKey(dto.getProjectUuid(), dto.getBranchKey()), BranchMeasuresDto::getBranchUuid)); | |||
List<NewCodePeriodDto> newCodePeriodDtos = dbClient.newCodePeriodDao().selectAll(dbSession); | |||
@@ -291,7 +291,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { | |||
private void resolveProjectStatistics(TelemetryData.Builder data, DbSession dbSession, String defaultQualityGateUuid, List<ProjectDto> projects) { | |||
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, ProjectAlmKeyAndProject> almAndUrlAndMonorepoByProject = getAlmAndUrlByProject(dbSession); | |||
Map<String, PrBranchAnalyzedLanguageCountByProjectDto> prAndBranchCountByProject = dbClient.branchDao().countPrBranchAnalyzedLanguageByProjectUuid(dbSession) | |||
.stream().collect(toMap(PrBranchAnalyzedLanguageCountByProjectDto::getProjectUuid, Function.identity())); | |||
Map<String, String> qgatesByProject = getProjectQgatesMap(dbSession); | |||
@@ -313,7 +313,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { | |||
.setQG(qgatesByProject.getOrDefault(projectUuid, defaultQualityGateUuid)) | |||
.setScm(Optional.ofNullable(scmByProject.get(projectUuid)).orElse(UNDETECTED)) | |||
.setCi(Optional.ofNullable(ciByProject.get(projectUuid)).orElse(UNDETECTED)) | |||
.setDevops(resolveDevopsPlatform(almAndUrlByProject, projectUuid)) | |||
.setDevops(resolveDevopsPlatform(almAndUrlAndMonorepoByProject, projectUuid)) | |||
.setBugs(metrics.getOrDefault("bugs", null)) | |||
.setDevelopmentCost(metrics.getOrDefault("development_cost", null)) | |||
.setVulnerabilities(metrics.getOrDefault("vulnerabilities", null)) | |||
@@ -322,6 +322,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { | |||
.setNcdId(ncdByProject.getOrDefault(projectUuid, instanceNcd).hashCode()) | |||
.setExternalSecurityReportExportedAt(securityReportExportedAtByProjectUuid.get(projectUuid)) | |||
.setCreationMethod(project.getCreationMethod()) | |||
.setMonorepo(resolveMonorepo(almAndUrlAndMonorepoByProject, projectUuid)) | |||
.build(); | |||
projectStatistics.add(stats); | |||
} | |||
@@ -343,6 +344,12 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { | |||
return UNDETECTED; | |||
} | |||
private static Boolean resolveMonorepo(Map<String, ProjectAlmKeyAndProject> almAndUrlByProject, String projectUuid) { | |||
return Optional.ofNullable(almAndUrlByProject.get(projectUuid)) | |||
.map(ProjectAlmKeyAndProject::getMonorepo) | |||
.orElse(false); | |||
} | |||
private void resolveProjects(TelemetryData.Builder data, DbSession dbSession) { | |||
Map<String, String> metricUuidMap = getNclocMetricUuidMap(dbSession); | |||
String nclocUuid = metricUuidMap.get(NCLOC_KEY); | |||
@@ -413,8 +420,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { | |||
Condition telemetryCondition = new Condition( | |||
metricKey, | |||
fromDbValue(condition.getOperator()), | |||
condition.getErrorThreshold() | |||
); | |||
condition.getErrorThreshold()); | |||
conditionsMap | |||
.computeIfAbsent(qualityGateUuid, k -> new ArrayList<>()) |