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.

InheritanceAction.java 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 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.qualityprofile.ws;
  21. import java.util.ArrayList;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;
  25. import org.sonar.api.resources.Languages;
  26. import org.sonar.api.server.ws.Change;
  27. import org.sonar.api.server.ws.Request;
  28. import org.sonar.api.server.ws.Response;
  29. import org.sonar.api.server.ws.WebService.NewAction;
  30. import org.sonar.api.server.ws.WebService.NewController;
  31. import org.sonar.db.DbClient;
  32. import org.sonar.db.DbSession;
  33. import org.sonar.db.qualityprofile.ActiveRuleCountQuery;
  34. import org.sonar.db.qualityprofile.ActiveRuleDao;
  35. import org.sonar.db.qualityprofile.QProfileDto;
  36. import org.sonarqube.ws.Qualityprofiles.InheritanceWsResponse;
  37. import org.sonarqube.ws.Qualityprofiles.InheritanceWsResponse.QualityProfile;
  38. import static java.util.Collections.singleton;
  39. import static java.util.Optional.ofNullable;
  40. import static org.sonar.db.qualityprofile.ActiveRuleDto.OVERRIDES;
  41. import static org.sonar.server.ws.WsUtils.writeProtobuf;
  42. public class InheritanceAction implements QProfileWsAction {
  43. private final DbClient dbClient;
  44. private final QProfileWsSupport wsSupport;
  45. private final Languages languages;
  46. public InheritanceAction(DbClient dbClient, QProfileWsSupport wsSupport, Languages languages) {
  47. this.dbClient = dbClient;
  48. this.wsSupport = wsSupport;
  49. this.languages = languages;
  50. }
  51. @Override
  52. public void define(NewController context) {
  53. NewAction inheritance = context.createAction("inheritance")
  54. .setSince("5.2")
  55. .setDescription("Show a quality profile's ancestors and children.")
  56. .setChangelog(new Change("10.3", "Field 'inactiveRuleCount' added to the response"))
  57. .setHandler(this)
  58. .setResponseExample(getClass().getResource("inheritance-example.json"));
  59. QProfileReference.defineParams(inheritance, languages);
  60. }
  61. @Override
  62. public void handle(Request request, Response response) throws Exception {
  63. QProfileReference reference = QProfileReference.fromName(request);
  64. try (DbSession dbSession = dbClient.openSession(false)) {
  65. QProfileDto profile = wsSupport.getProfile(dbSession, reference);
  66. List<QProfileDto> ancestors = ancestors(profile, dbSession);
  67. List<QProfileDto> children = dbClient.qualityProfileDao().selectChildren(dbSession, singleton(profile));
  68. List<QProfileDto> allProfiles = new ArrayList<>();
  69. allProfiles.add(profile);
  70. allProfiles.addAll(ancestors);
  71. allProfiles.addAll(children);
  72. Statistics statistics = new Statistics(dbSession, allProfiles, profile.getLanguage());
  73. writeProtobuf(buildResponse(profile, ancestors, children, statistics), request, response);
  74. }
  75. }
  76. private List<QProfileDto> ancestors(QProfileDto profile, DbSession dbSession) {
  77. List<QProfileDto> ancestors = new ArrayList<>();
  78. collectAncestors(profile, ancestors, dbSession);
  79. return ancestors;
  80. }
  81. private void collectAncestors(QProfileDto profile, List<QProfileDto> ancestors, DbSession session) {
  82. if (profile.getParentKee() == null) {
  83. return;
  84. }
  85. QProfileDto parent = getParent(session, profile);
  86. ancestors.add(parent);
  87. collectAncestors(parent, ancestors, session);
  88. }
  89. private QProfileDto getParent(DbSession dbSession, QProfileDto profile) {
  90. QProfileDto parent = dbClient.qualityProfileDao().selectByUuid(dbSession, profile.getParentKee());
  91. if (parent == null) {
  92. throw new IllegalStateException("Cannot find parent of profile: " + profile.getKee());
  93. }
  94. return parent;
  95. }
  96. private static InheritanceWsResponse buildResponse(QProfileDto profile, List<QProfileDto> ancestors, List<QProfileDto> children, Statistics statistics) {
  97. return InheritanceWsResponse.newBuilder()
  98. .setProfile(buildProfile(profile, statistics))
  99. .addAllAncestors(buildAncestors(ancestors, statistics))
  100. .addAllChildren(buildChildren(children, statistics))
  101. .build();
  102. }
  103. private static Iterable<QualityProfile> buildAncestors(List<QProfileDto> ancestors, Statistics statistics) {
  104. return ancestors.stream()
  105. .map(ancestor -> buildProfile(ancestor, statistics))
  106. .toList();
  107. }
  108. private static Iterable<QualityProfile> buildChildren(List<QProfileDto> children, Statistics statistics) {
  109. return children.stream()
  110. .map(child -> buildProfile(child, statistics))
  111. .toList();
  112. }
  113. private static QualityProfile buildProfile(QProfileDto qualityProfile, Statistics statistics) {
  114. String key = qualityProfile.getKee();
  115. QualityProfile.Builder builder = QualityProfile.newBuilder()
  116. .setKey(key)
  117. .setName(qualityProfile.getName())
  118. .setActiveRuleCount(statistics.countRulesByProfileKey.getOrDefault(key, 0L))
  119. .setOverridingRuleCount(statistics.countOverridingRulesByProfileKey.getOrDefault(key, 0L))
  120. .setInactiveRuleCount(statistics.countInactiveRuleByProfileKey.getOrDefault(key, 0L))
  121. .setIsBuiltIn(qualityProfile.isBuiltIn());
  122. ofNullable(qualityProfile.getParentKee()).ifPresent(builder::setParent);
  123. return builder.build();
  124. }
  125. private class Statistics {
  126. private final Map<String, Long> countRulesByProfileKey;
  127. private final Map<String, Long> countOverridingRulesByProfileKey;
  128. private final Map<String, Long> countInactiveRuleByProfileKey = new HashMap<>();
  129. private Statistics(DbSession dbSession, List<QProfileDto> profiles, String language) {
  130. ActiveRuleDao dao = dbClient.activeRuleDao();
  131. ActiveRuleCountQuery.Builder builder = ActiveRuleCountQuery.builder();
  132. countRulesByProfileKey = dao.countActiveRulesByQuery(dbSession, builder.setProfiles(profiles).build());
  133. countOverridingRulesByProfileKey = dao.countActiveRulesByQuery(dbSession, builder.setProfiles(profiles).setInheritance(OVERRIDES).build());
  134. long totalRuleAvailable = dbClient.ruleDao().countByLanguage(dbSession, language);
  135. countRulesByProfileKey.forEach((profileKey, activeRules) -> countInactiveRuleByProfileKey.put(profileKey, totalRuleAvailable - activeRules));
  136. }
  137. }
  138. }