3 * Copyright (C) 2009-2019 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.qualityprofile.ws;
22 import java.util.ArrayList;
23 import java.util.List;
25 import java.util.stream.Collectors;
26 import org.sonar.api.resources.Languages;
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.organization.OrganizationDto;
34 import org.sonar.db.qualityprofile.ActiveRuleCountQuery;
35 import org.sonar.db.qualityprofile.ActiveRuleDao;
36 import org.sonar.db.qualityprofile.QProfileDto;
37 import org.sonarqube.ws.Qualityprofiles.InheritanceWsResponse;
38 import org.sonarqube.ws.Qualityprofiles.InheritanceWsResponse.QualityProfile;
40 import static java.util.Collections.singleton;
41 import static java.util.Optional.ofNullable;
42 import static org.sonar.db.qualityprofile.ActiveRuleDto.OVERRIDES;
43 import static org.sonar.server.qualityprofile.ws.QProfileWsSupport.createOrganizationParam;
44 import static org.sonar.server.ws.WsUtils.writeProtobuf;
46 public class InheritanceAction implements QProfileWsAction {
48 private final DbClient dbClient;
49 private final QProfileWsSupport wsSupport;
50 private final Languages languages;
52 public InheritanceAction(DbClient dbClient, QProfileWsSupport wsSupport, Languages languages) {
53 this.dbClient = dbClient;
54 this.wsSupport = wsSupport;
55 this.languages = languages;
59 public void define(NewController context) {
60 NewAction inheritance = context.createAction("inheritance")
62 .setDescription("Show a quality profile's ancestors and children.")
64 .setResponseExample(getClass().getResource("inheritance-example.json"));
66 createOrganizationParam(inheritance)
68 QProfileReference.defineParams(inheritance, languages);
72 public void handle(Request request, Response response) throws Exception {
73 QProfileReference reference = QProfileReference.from(request);
74 try (DbSession dbSession = dbClient.openSession(false)) {
75 QProfileDto profile = wsSupport.getProfile(dbSession, reference);
76 OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
77 List<QProfileDto> ancestors = ancestors(profile, dbSession);
78 List<QProfileDto> children = dbClient.qualityProfileDao().selectChildren(dbSession, singleton(profile));
79 List<QProfileDto> allProfiles = new ArrayList<>();
80 allProfiles.add(profile);
81 allProfiles.addAll(ancestors);
82 allProfiles.addAll(children);
83 Statistics statistics = new Statistics(dbSession, organization, allProfiles);
85 writeProtobuf(buildResponse(profile, ancestors, children, statistics), request, response);
89 private List<QProfileDto> ancestors(QProfileDto profile, DbSession dbSession) {
90 List<QProfileDto> ancestors = new ArrayList<>();
91 collectAncestors(profile, ancestors, dbSession);
95 private void collectAncestors(QProfileDto profile, List<QProfileDto> ancestors, DbSession session) {
96 if (profile.getParentKee() == null) {
100 QProfileDto parent = getParent(session, profile);
101 ancestors.add(parent);
102 collectAncestors(parent, ancestors, session);
105 private QProfileDto getParent(DbSession dbSession, QProfileDto profile) {
106 QProfileDto parent = dbClient.qualityProfileDao().selectByUuid(dbSession, profile.getParentKee());
107 if (parent == null) {
108 throw new IllegalStateException("Cannot find parent of profile: " + profile.getKee());
113 private static InheritanceWsResponse buildResponse(QProfileDto profile, List<QProfileDto> ancestors, List<QProfileDto> children, Statistics statistics) {
114 return InheritanceWsResponse.newBuilder()
115 .setProfile(buildProfile(profile, statistics))
116 .addAllAncestors(buildAncestors(ancestors, statistics))
117 .addAllChildren(buildChildren(children, statistics))
121 private static Iterable<QualityProfile> buildAncestors(List<QProfileDto> ancestors, Statistics statistics) {
122 return ancestors.stream()
123 .map(ancestor -> buildProfile(ancestor, statistics))
124 .collect(Collectors.toList());
127 private static Iterable<QualityProfile> buildChildren(List<QProfileDto> children, Statistics statistics) {
128 return children.stream()
129 .map(child -> buildProfile(child, statistics))
130 .collect(Collectors.toList());
133 private static QualityProfile buildProfile(QProfileDto qualityProfile, Statistics statistics) {
134 String key = qualityProfile.getKee();
135 QualityProfile.Builder builder = QualityProfile.newBuilder()
137 .setName(qualityProfile.getName())
138 .setActiveRuleCount(statistics.countRulesByProfileKey.getOrDefault(key, 0L))
139 .setOverridingRuleCount(statistics.countOverridingRulesByProfileKey.getOrDefault(key, 0L))
140 .setIsBuiltIn(qualityProfile.isBuiltIn());
141 ofNullable(qualityProfile.getParentKee()).ifPresent(builder::setParent);
142 return builder.build();
145 private class Statistics {
146 private final Map<String, Long> countRulesByProfileKey;
147 private final Map<String, Long> countOverridingRulesByProfileKey;
149 private Statistics(DbSession dbSession, OrganizationDto organization, List<QProfileDto> profiles) {
150 ActiveRuleDao dao = dbClient.activeRuleDao();
151 ActiveRuleCountQuery.Builder builder = ActiveRuleCountQuery.builder().setOrganization(organization);
152 countRulesByProfileKey = dao.countActiveRulesByQuery(dbSession, builder.setProfiles(profiles).build());
153 countOverridingRulesByProfileKey = dao.countActiveRulesByQuery(dbSession, builder.setProfiles(profiles).setInheritance(OVERRIDES).build());