]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16316 Add quality gate and quality profile findings
authorKlaudio Sinani <klaudio.sinani@sonarsource.com>
Mon, 23 May 2022 08:22:28 +0000 (10:22 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 25 May 2022 20:03:16 +0000 (20:03 +0000)
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/report/QualityGateFindingDto.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/report/QualityProfileFindingDto.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/report/RegulatoryReportDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/report/RegulatoryReportMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/report/RegulatoryReportMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/report/RegulatoryReportDaoTest.java

index b692d37f315b41b9e58e35d4df4483fa13f68f26..2948705b2a910b5516f75f452fce3f5ae06b7cc0 100644 (file)
@@ -167,6 +167,10 @@ public class QualityProfileDao implements Dao {
     return selectAssociatedToProjectUuidAndLanguages(dbSession, project.getUuid(), languages);
   }
 
+  public List<String> selectQProfileUuidsByProjectUuid(DbSession dbSession, String projectUuid) {
+    return mapper(dbSession).selectQProfileUuidsByProjectUuid(projectUuid);
+  }
+
   public List<QProfileDto> selectByLanguage(DbSession dbSession, String language) {
     return mapper(dbSession).selectByLanguage(language);
   }
index e3261915a865297540b0cb574fb61842c2727631..96bbb6a7efb153d908fbc20d58109eadb991cff0 100644 (file)
@@ -91,6 +91,8 @@ public interface QualityProfileMapper {
     @Param("projectUuid") String projectUuid,
     @Param("languages") Collection<String> languages);
 
+  List<String> selectQProfileUuidsByProjectUuid(@Param("projectUuid") String projectUuid);
+
   void insertProjectProfileAssociation(
     @Param("uuid") String uuid,
     @Param("projectUuid") String projectUuid,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/report/QualityGateFindingDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/report/QualityGateFindingDto.java
new file mode 100644 (file)
index 0000000..1a21276
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.report;
+
+import javax.annotation.CheckForNull;
+
+public class QualityGateFindingDto {
+  public static final String NEW_CODE_METRIC_PREFIX = "new_";
+
+  private String description = null;
+  private String operator = null;
+  private String kee = null;
+  private Boolean isEnabled = null;
+  private String valueType = null;
+  private Double bestValue = null;
+  private Double worstValue = null;
+  private Boolean optimizedBestValue = null;
+  private String errorThreshold = null;
+  private Integer decimalScale = null;
+
+  private String getOperator() {
+    return operator;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public String getOperatorDescription() {
+    return OperatorDescription.valueOf(getOperator()).getDescription();
+  }
+
+  public Boolean isNewCodeMetric() {
+    return kee.startsWith(NEW_CODE_METRIC_PREFIX);
+  }
+
+  public boolean isEnabled() {
+    return isEnabled;
+  }
+
+  public String getValueType() {
+    return valueType;
+  }
+
+  @CheckForNull
+  public Double getBestValue() {
+    return bestValue;
+  }
+
+  @CheckForNull
+  public Double getWorstValue() {
+    return worstValue;
+  }
+
+  public String getErrorThreshold() {
+    return errorThreshold;
+  }
+
+  public boolean isOptimizedBestValue() {
+    return optimizedBestValue;
+  }
+
+  @CheckForNull
+  public Integer getDecimalScale() {
+    return decimalScale;
+  }
+
+  public enum OperatorDescription {
+    LT("Is Less Than"),
+    GT("Is Greater Than"),
+    EQ("Is Equal To"),
+    NE("Is Not Equal To");
+
+    private final String desc;
+
+    OperatorDescription(String desc) {
+      this.desc = desc;
+    }
+
+    public String getDescription() {
+      return desc;
+    }
+  }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/report/QualityProfileFindingDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/report/QualityProfileFindingDto.java
new file mode 100644 (file)
index 0000000..43017f1
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.report;
+
+import javax.annotation.CheckForNull;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rules.RuleType;
+import org.sonar.db.rule.SeverityUtil;
+
+public class QualityProfileFindingDto {
+  private String language = null;
+  private String title = null;
+  private String repository = null;
+  private String ruleKey = null;
+  private String status = null;
+  private Integer type = null;
+  private Integer severity = null;
+
+  public String getLanguage() {
+    return language;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  public RuleKey getReferenceKey() {
+    return RuleKey.of(repository, ruleKey);
+  }
+
+  public RuleStatus getStatus() {
+    return RuleStatus.valueOf(status);
+  }
+
+  public RuleType getType() {
+    return RuleType.valueOf(type);
+  }
+
+  @CheckForNull
+  public String getSeverity() {
+    return SeverityUtil.getSeverityFromOrdinal(severity);
+  }
+}
index 8cd4a7c350b7ffd0e675c8ba1de931e960dbf320..e7eb4291de1b6439ce4a76a6231563d09cab238a 100644 (file)
@@ -28,6 +28,14 @@ public class RegulatoryReportDao implements Dao {
     mapper(dbSession).scrollIssues(branchUuid, handler);
   }
 
+  public void getQualityGateFindings(DbSession dbSession, String qualityGateUuid, ResultHandler<QualityGateFindingDto> handler) {
+    mapper(dbSession).getQualityGateFindings(qualityGateUuid, handler);
+  }
+
+  public void getQualityProfileFindings(DbSession dbSession, String qualityProfileUuid, ResultHandler<QualityProfileFindingDto> handler) {
+    mapper(dbSession).getQualityProfileFindings(qualityProfileUuid, handler);
+  }
+
   private static RegulatoryReportMapper mapper(DbSession dbSession) {
     return dbSession.getMapper(RegulatoryReportMapper.class);
   }
index 1a75e22bdcb4db5f8212c75ff0261f9033b7c7cf..a73fddc21ddc329b63f57035eb44e4e1da240a27 100644 (file)
@@ -23,4 +23,8 @@ import org.apache.ibatis.session.ResultHandler;
 
 public interface RegulatoryReportMapper {
   void scrollIssues(String branchUuid, ResultHandler<IssueFindingDto> handler);
+
+  void getQualityGateFindings(String qualityGateUuid, ResultHandler<QualityGateFindingDto> handler);
+
+  void getQualityProfileFindings(String qualityProfileUuid, ResultHandler<QualityProfileFindingDto> handler);
 }
index e6dabc0aa382e909ed2929ab941f414f71359d53..db706ae4d65b09e4cbad56ff3a09721fee176cfc 100644 (file)
       and pqp.project_uuid = #{projectUuid, jdbcType=VARCHAR}
   </select>
 
+  <select id="selectQProfileUuidsByProjectUuid" parameterType="String" resultType="String">
+    SELECT profile_key
+    FROM project_qprofiles
+    <where>
+      AND project_uuid=#{projectUuid}
+    </where>
+  </select>
+
   <insert id="insertProjectProfileAssociation" useGeneratedKeys="false">
     insert into project_qprofiles (
       uuid,
index 84787bdc8ca3ea9e5ee1895e821c1e4fad49bf0f..c598bcc587c87939b548c26576a14c495e502c88 100644 (file)
@@ -3,6 +3,30 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
 
 <mapper namespace="org.sonar.db.report.RegulatoryReportMapper">
+  <sql id="qualityGateFindingColumns">
+    m.description as description,
+    qgc.operator as operator,
+    m.name as kee,
+    m.enabled as isEnabled,
+    m.val_type as valueType,
+    m.best_value as bestValue,
+    m.worst_value as worstValue,
+    m.optimized_best_value as optimizedBestValue,
+    qgc.value_error as errorThreshold,
+    m.decimal_scale as decimalScale
+  </sql>
+
+  <sql id="qualityProfileFindingColumns">
+    r.language as language,
+    r.name as title,
+    r.plugin_name as repository,
+    r.plugin_rule_key as ruleKey,
+    r.status as status,
+    r.rule_type as type,
+    r.priority as defaultSeverity,
+    ar.failure_level as severity
+  </sql>
+
   <sql id="issueColumns">
     i.kee as kee,
     i.severity as severity,
     <include refid="org.sonar.db.issue.IssueMapper.isNewCodeReferenceIssue"/>
   </sql>
 
+  <select id="getQualityGateFindings" parameterType="String" resultType="org.sonar.db.report.QualityGateFindingDto">
+    select
+    <include refid="qualityGateFindingColumns"/>
+    from quality_gates qg
+    left join quality_gate_conditions qgc on qgc.qgate_uuid = qg.uuid
+    left join metrics m on m.uuid = qgc.metric_uuid
+    where qg.uuid=#{qualityGateUuid,jdbcType=VARCHAR}
+  </select>
+
+  <select id="getQualityProfileFindings" parameterType="String" resultType="org.sonar.db.report.QualityProfileFindingDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+    select
+    <include refid="qualityProfileFindingColumns"/>
+    from rules r
+    left join active_rules ar on ar.rule_uuid = r.uuid
+    inner join org_qprofiles oqp on oqp.rules_profile_uuid = ar.profile_uuid
+    where oqp.uuid=#{qualityProfileUuid,jdbcType=VARCHAR}
+  </select>
+
   <select id="scrollIssues" parameterType="String" resultType="org.sonar.db.report.IssueFindingDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
     select
     <include refid="issueColumns"/>
@@ -32,5 +74,4 @@
     where i.project_uuid=#{branchUuid,jdbcType=VARCHAR}
     and i.status !='CLOSED'
   </select>
-
 </mapper>
index 026fb17668eead6c424cccdb3fa239bfa6ec765d..2ea5e44292cc041a18b54147bcc45b979cc44e6d 100644 (file)
@@ -670,6 +670,29 @@ public class QualityProfileDaoTest {
       .isEmpty();
   }
 
+  @Test
+  public void test_selectQProfileUuidsByProjectUuid() {
+    ProjectDto project1 = db.components().insertPublicProjectDto();
+    ProjectDto project2 = db.components().insertPublicProjectDto();
+    ProjectDto project3 = db.components().insertPublicProjectDto();
+    QProfileDto javaProfile = db.qualityProfiles().insert(p -> p.setLanguage("java"));
+    QProfileDto jsProfile = db.qualityProfiles().insert(p -> p.setLanguage("js"));
+    QProfileDto cProfile = db.qualityProfiles().insert(p -> p.setLanguage("c"));
+    db.qualityProfiles().associateWithProject(project1, javaProfile, cProfile);
+    db.qualityProfiles().associateWithProject(project2, jsProfile);
+
+    assertThat(underTest.selectQProfileUuidsByProjectUuid(dbSession, project1.getUuid()))
+      .hasSize(2)
+      .containsExactly(javaProfile.getKee(), cProfile.getKee());
+
+    assertThat(underTest.selectQProfileUuidsByProjectUuid(dbSession, project2.getUuid()))
+      .hasSize(1)
+      .containsExactly(jsProfile.getKee());
+
+    assertThat(underTest.selectQProfileUuidsByProjectUuid(dbSession, project3.getUuid()))
+      .isEmpty();
+  }
+
   @Test
   public void test_updateProjectProfileAssociation() {
     ProjectDto project = db.components().insertPrivateProjectDto();
index 81411f30ae40ccbe9c90a1bd6b8aae82b50ddc9c..fba4390d17a6d43164ec581fc6987ec5948327fe 100644 (file)
@@ -21,24 +21,40 @@ package org.sonar.db.report;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.issue.IssueDto;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.qualitygate.QualityGateConditionDto;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.SeverityUtil;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.within;
 import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.report.QualityGateFindingDto.NEW_CODE_METRIC_PREFIX;
 
 public class RegulatoryReportDaoTest {
   private static final String PROJECT_UUID = "prj_uuid";
   private static final String PROJECT_KEY = "prj_key";
   private static final String FILE_UUID = "file_uuid";
   private static final String FILE_KEY = "file_key";
+  private static final String BRANCH_UUID = "branch_uuid";
+  private static final String BRANCH_NAME = "branch";
 
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
@@ -55,6 +71,94 @@ public class RegulatoryReportDaoTest {
     file = db.components().insertComponent(newFileDto(project).setUuid(FILE_UUID).setDbKey(FILE_KEY));
   }
 
+  @Test
+  public void getQualityGateFindings_returns_all_quality_gate_details_for_project() {
+    ProjectDto project = db.components().insertPublicProjectDto();
+    BranchDto branch = db.components().insertProjectBranch(project).setBranchType(BranchType.BRANCH);
+    QualityGateDto gate = db.qualityGates().insertQualityGate();
+    db.qualityGates().setDefaultQualityGate(gate);
+
+    MetricDto metric1 = db.measures().insertMetric(m -> m.setDescription("metric 1 description").setDecimalScale(0));
+    QualityGateConditionDto condition1 = db.qualityGates().addCondition(gate, metric1);
+
+    MetricDto metric2 = db.measures().insertMetric(m -> m.setDescription("metric 2 description").setDecimalScale(1));
+    QualityGateConditionDto condition2 = db.qualityGates().addCondition(gate, metric2);
+
+    MetricDto metric3 = db.measures().insertMetric(m -> m.setDescription("metric 3 description").setDecimalScale(0));
+    QualityGateConditionDto condition3 = db.qualityGates().addCondition(gate, metric3);
+
+    db.qualityGates().associateProjectToQualityGate(project, gate);
+    db.commit();
+
+    List<QualityGateFindingDto> findings = new ArrayList<>();
+    underTest.getQualityGateFindings(db.getSession(), gate.getUuid(), result -> findings.add(result.getResultObject()));
+
+    QualityGateFindingDto finding = findings.stream().filter(f -> f.getDescription().equals("metric 1 description")).findFirst().get();
+
+    // check fields
+    assertThat(findings).hasSize(3);
+    assertThat(findings.stream().map(f -> f.getDescription()).collect(Collectors.toSet())).containsExactlyInAnyOrder("metric 1 description", "metric 2 description", "metric 3 description");
+    assertThat(finding.getDescription()).isEqualTo(metric1.getDescription());
+    assertThat(finding.getOperatorDescription()).isEqualTo(QualityGateFindingDto.OperatorDescription.valueOf(condition1.getOperator()).getDescription());
+    assertThat(finding.getErrorThreshold()).isEqualTo(condition1.getErrorThreshold());
+    assertThat(finding.getValueType()).isEqualTo(metric1.getValueType());
+    assertThat(finding.isNewCodeMetric()).isEqualTo(metric1.getKey().startsWith(NEW_CODE_METRIC_PREFIX));
+    assertThat(finding.isEnabled()).isEqualTo(metric1.isEnabled());
+    assertThat(finding.getBestValue()).isEqualTo(metric1.getBestValue(), within(0.00001));
+    assertThat(finding.getWorstValue()).isEqualTo(metric1.getWorstValue(), within(0.00001));
+    assertThat(finding.isOptimizedBestValue()).isEqualTo(metric1.isOptimizedBestValue());
+    assertThat(finding.getDecimalScale()).isEqualTo(metric1.getDecimalScale());
+  }
+
+  @Test
+  public void getQualityProfileFindings_returns_all_quality_profile_details_for_project() {
+    String projectUuid = "project_uuid";
+    String projectKey = "project_key";
+    String branchUuid = "branch_uuid";
+    String branchName = "branch";
+
+    BranchDto branch = new BranchDto()
+      .setBranchType(BranchType.BRANCH)
+      .setKey(branchName)
+      .setUuid(branchUuid)
+      .setProjectUuid(projectUuid);
+
+    db.getDbClient().branchDao().insert(db.getSession(), branch);
+
+    ProjectDto project = db.components().insertPublicProjectDto(t -> t.setProjectUuid(projectUuid)
+      .setUuid(projectUuid)
+      .setDbKey(projectKey)
+      .setMainBranchProjectUuid(branchUuid));
+
+    QProfileDto cppQPWithoutActiveRules = db.qualityProfiles().insert(qp -> qp.setIsBuiltIn(true).setLanguage("cpp"));
+    db.qualityProfiles().setAsDefault(cppQPWithoutActiveRules);
+
+    QProfileDto javaBuiltInQPWithActiveRules = db.qualityProfiles().insert(qp -> qp.setIsBuiltIn(true).setLanguage("java"));
+    RuleDto rule1 = db.rules().insert(r -> r.setName("rule 1 title"));
+    ActiveRuleDto activeRule1 = db.qualityProfiles().activateRule(javaBuiltInQPWithActiveRules, rule1);
+    RuleDto rule2 = db.rules().insert(r -> r.setName("rule 2 title"));
+    ActiveRuleDto activeRule2 = db.qualityProfiles().activateRule(javaBuiltInQPWithActiveRules, rule2);
+    RuleDto rule3 = db.rules().insert(r -> r.setName("rule 3 title"));
+    ActiveRuleDto activeRule3 = db.qualityProfiles().activateRule(javaBuiltInQPWithActiveRules, rule3);
+
+    db.qualityProfiles().associateWithProject(project, cppQPWithoutActiveRules, javaBuiltInQPWithActiveRules);
+    db.getSession().commit();
+
+    List<QualityProfileFindingDto> findings = new ArrayList<>();
+    underTest.getQualityProfileFindings(db.getSession(), javaBuiltInQPWithActiveRules.getKee(), result -> findings.add(result.getResultObject()));
+
+    QualityProfileFindingDto finding = findings.stream().filter(f -> f.getTitle().equals("rule 1 title")).findFirst().get();
+
+    assertThat(findings).hasSize(3);
+    assertThat(findings.stream().map(f -> f.getTitle()).collect(Collectors.toSet())).containsExactlyInAnyOrder("rule 1 title", "rule 2 title", "rule 3 title");
+    assertThat(finding.getLanguage()).isEqualTo(rule1.getLanguage());
+    assertThat(finding.getTitle()).isEqualTo(rule1.getName());
+    assertThat(finding.getReferenceKey()).isEqualTo(RuleKey.of(rule1.getRepositoryKey(), rule1.getRuleKey()));
+    assertThat(finding.getStatus()).isEqualTo(rule1.getStatus());
+    assertThat(finding.getType()).isEqualTo(RuleType.valueOf(rule1.getType()));
+    assertThat(finding.getSeverity()).isEqualTo(SeverityUtil.getSeverityFromOrdinal(activeRule1.getSeverity()));
+  }
+
   @Test
   public void scrollIssues_returns_all_non_closed_issues_for_project() {
     IssueDto issue1 = db.issues().insertIssue(rule, project, file, i -> i.setStatus("OPEN").setResolution(null));