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.

SearchResponseFormat.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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.issue.ws;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.Date;
  24. import java.util.LinkedHashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Optional;
  28. import java.util.Set;
  29. import java.util.stream.Collectors;
  30. import org.sonar.api.resources.Language;
  31. import org.sonar.api.resources.Languages;
  32. import org.sonar.api.rule.RuleKey;
  33. import org.sonar.api.rules.CleanCodeAttribute;
  34. import org.sonar.api.rules.RuleType;
  35. import org.sonar.api.utils.DateUtils;
  36. import org.sonar.api.utils.Duration;
  37. import org.sonar.api.utils.Durations;
  38. import org.sonar.api.utils.Paging;
  39. import org.sonar.core.issue.status.IssueStatus;
  40. import org.sonar.db.component.BranchDto;
  41. import org.sonar.db.component.BranchType;
  42. import org.sonar.db.component.ComponentDto;
  43. import org.sonar.db.issue.IssueChangeDto;
  44. import org.sonar.db.issue.IssueDto;
  45. import org.sonar.db.project.ProjectDto;
  46. import org.sonar.db.protobuf.DbIssues;
  47. import org.sonar.db.rule.RuleDto;
  48. import org.sonar.db.user.UserDto;
  49. import org.sonar.markdown.Markdown;
  50. import org.sonar.server.es.Facets;
  51. import org.sonar.server.issue.TextRangeResponseFormatter;
  52. import org.sonar.server.issue.index.IssueScope;
  53. import org.sonar.server.issue.workflow.Transition;
  54. import org.sonar.server.ws.MessageFormattingUtils;
  55. import org.sonarqube.ws.Common;
  56. import org.sonarqube.ws.Common.Comment;
  57. import org.sonarqube.ws.Common.User;
  58. import org.sonarqube.ws.Issues;
  59. import org.sonarqube.ws.Issues.Actions;
  60. import org.sonarqube.ws.Issues.Comments;
  61. import org.sonarqube.ws.Issues.Component;
  62. import org.sonarqube.ws.Issues.Issue;
  63. import org.sonarqube.ws.Issues.Operation;
  64. import org.sonarqube.ws.Issues.SearchWsResponse;
  65. import org.sonarqube.ws.Issues.Transitions;
  66. import org.sonarqube.ws.Issues.Users;
  67. import static com.google.common.base.MoreObjects.firstNonNull;
  68. import static com.google.common.base.Preconditions.checkState;
  69. import static com.google.common.base.Strings.emptyToNull;
  70. import static com.google.common.base.Strings.nullToEmpty;
  71. import static java.lang.String.format;
  72. import static java.util.Collections.emptyList;
  73. import static java.util.Objects.requireNonNull;
  74. import static java.util.Optional.ofNullable;
  75. import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
  76. import static org.sonar.api.rule.RuleKey.EXTERNAL_RULE_REPO_PREFIX;
  77. import static org.sonar.server.issue.index.IssueIndex.FACET_ASSIGNED_TO_ME;
  78. import static org.sonar.server.issue.index.IssueIndex.FACET_PROJECTS;
  79. import static org.sonar.server.issue.ws.SearchAdditionalField.ACTIONS;
  80. import static org.sonar.server.issue.ws.SearchAdditionalField.ALL_ADDITIONAL_FIELDS;
  81. import static org.sonar.server.issue.ws.SearchAdditionalField.COMMENTS;
  82. import static org.sonar.server.issue.ws.SearchAdditionalField.RULE_DESCRIPTION_CONTEXT_KEY;
  83. import static org.sonar.server.issue.ws.SearchAdditionalField.TRANSITIONS;
  84. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
  85. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
  86. public class SearchResponseFormat {
  87. private final Durations durations;
  88. private final Languages languages;
  89. private final TextRangeResponseFormatter textRangeFormatter;
  90. private final UserResponseFormatter userFormatter;
  91. public SearchResponseFormat(Durations durations, Languages languages, TextRangeResponseFormatter textRangeFormatter, UserResponseFormatter userFormatter) {
  92. this.durations = durations;
  93. this.languages = languages;
  94. this.textRangeFormatter = textRangeFormatter;
  95. this.userFormatter = userFormatter;
  96. }
  97. SearchWsResponse formatSearch(Set<SearchAdditionalField> fields, SearchResponseData data, Paging paging, Facets facets) {
  98. SearchWsResponse.Builder response = SearchWsResponse.newBuilder();
  99. formatPaging(paging, response);
  100. ofNullable(data.getEffortTotal()).ifPresent(response::setEffortTotal);
  101. response.addAllIssues(createIssues(fields, data));
  102. response.addAllComponents(formatComponents(data));
  103. formatFacets(data, facets, response);
  104. if (fields.contains(SearchAdditionalField.RULES)) {
  105. response.setRules(formatRules(data));
  106. }
  107. if (fields.contains(SearchAdditionalField.USERS)) {
  108. response.setUsers(formatUsers(data));
  109. }
  110. if (fields.contains(SearchAdditionalField.LANGUAGES)) {
  111. response.setLanguages(formatLanguages());
  112. }
  113. return response.build();
  114. }
  115. Issues.ListWsResponse formatList(Set<SearchAdditionalField> fields, SearchResponseData data, Paging paging) {
  116. Issues.ListWsResponse.Builder response = Issues.ListWsResponse.newBuilder();
  117. response.setPaging(Common.Paging.newBuilder()
  118. .setPageIndex(paging.pageIndex())
  119. .setPageSize(data.getIssues().size()));
  120. response.addAllIssues(createIssues(fields, data));
  121. response.addAllComponents(formatComponents(data));
  122. return response.build();
  123. }
  124. Operation formatOperation(SearchResponseData data) {
  125. Operation.Builder response = Operation.newBuilder();
  126. if (data.getIssues().size() == 1) {
  127. IssueDto dto = data.getIssues().get(0);
  128. response.setIssue(createIssue(ALL_ADDITIONAL_FIELDS, data, dto));
  129. }
  130. response.addAllComponents(formatComponents(data));
  131. response.addAllRules(formatRules(data).getRulesList());
  132. response.addAllUsers(formatUsers(data).getUsersList());
  133. return response.build();
  134. }
  135. private static void formatPaging(Paging paging, SearchWsResponse.Builder response) {
  136. response.setP(paging.pageIndex());
  137. response.setPs(paging.pageSize());
  138. response.setTotal(paging.total());
  139. response.setPaging(formatPaging(paging));
  140. }
  141. private static Common.Paging.Builder formatPaging(Paging paging) {
  142. return Common.Paging.newBuilder()
  143. .setPageIndex(paging.pageIndex())
  144. .setPageSize(paging.pageSize())
  145. .setTotal(paging.total());
  146. }
  147. private List<Issues.Issue> createIssues(Collection<SearchAdditionalField> fields, SearchResponseData data) {
  148. return data.getIssues().stream()
  149. .map(dto -> createIssue(fields, data, dto))
  150. .toList();
  151. }
  152. private Issue createIssue(Collection<SearchAdditionalField> fields, SearchResponseData data, IssueDto dto) {
  153. Issue.Builder issueBuilder = Issue.newBuilder();
  154. addMandatoryFieldsToIssueBuilder(issueBuilder, dto, data);
  155. addAdditionalFieldsToIssueBuilder(fields, data, dto, issueBuilder);
  156. return issueBuilder.build();
  157. }
  158. private void addMandatoryFieldsToIssueBuilder(Issue.Builder issueBuilder, IssueDto dto, SearchResponseData data) {
  159. issueBuilder.setKey(dto.getKey());
  160. issueBuilder.setType(Common.RuleType.forNumber(dto.getType()));
  161. CleanCodeAttribute cleanCodeAttribute = dto.getEffectiveCleanCodeAttribute();
  162. if (cleanCodeAttribute != null) {
  163. issueBuilder.setCleanCodeAttribute(Common.CleanCodeAttribute.valueOf(cleanCodeAttribute.name()));
  164. issueBuilder.setCleanCodeAttributeCategory(Common.CleanCodeAttributeCategory.valueOf(cleanCodeAttribute.getAttributeCategory().name()));
  165. }
  166. issueBuilder.addAllImpacts(dto.getEffectiveImpacts().entrySet()
  167. .stream()
  168. .map(entry -> Common.Impact.newBuilder()
  169. .setSoftwareQuality(Common.SoftwareQuality.valueOf(entry.getKey().name()))
  170. .setSeverity(Common.ImpactSeverity.valueOf(entry.getValue().name()))
  171. .build())
  172. .toList());
  173. ComponentDto component = data.getComponentByUuid(dto.getComponentUuid());
  174. issueBuilder.setComponent(component.getKey());
  175. setBranchOrPr(component, issueBuilder, data);
  176. ComponentDto branch = data.getComponentByUuid(dto.getProjectUuid());
  177. if (branch != null) {
  178. issueBuilder.setProject(branch.getKey());
  179. }
  180. issueBuilder.setRule(dto.getRuleKey().toString());
  181. if (dto.isExternal()) {
  182. issueBuilder.setExternalRuleEngine(engineNameFrom(dto.getRuleKey()));
  183. }
  184. if (dto.getType() != RuleType.SECURITY_HOTSPOT.getDbConstant()) {
  185. issueBuilder.setSeverity(Common.Severity.valueOf(dto.getSeverity()));
  186. }
  187. ofNullable(data.getUserByUuid(dto.getAssigneeUuid())).ifPresent(assignee -> issueBuilder.setAssignee(assignee.getLogin()));
  188. ofNullable(emptyToNull(dto.getResolution())).ifPresent(issueBuilder::setResolution);
  189. issueBuilder.setStatus(dto.getStatus());
  190. Optional.ofNullable(IssueStatus.of(dto.getStatus(), dto.getResolution())).map(IssueStatus::name).ifPresent(issueBuilder::setIssueStatus);
  191. issueBuilder.setMessage(nullToEmpty(dto.getMessage()));
  192. issueBuilder.addAllMessageFormattings(MessageFormattingUtils.dbMessageFormattingToWs(dto.parseMessageFormattings()));
  193. issueBuilder.addAllTags(dto.getTags());
  194. issueBuilder.addAllCodeVariants(dto.getCodeVariants());
  195. Long effort = dto.getEffort();
  196. if (effort != null) {
  197. String effortValue = durations.encode(Duration.create(effort));
  198. issueBuilder.setDebt(effortValue);
  199. issueBuilder.setEffort(effortValue);
  200. }
  201. ofNullable(dto.getLine()).ifPresent(issueBuilder::setLine);
  202. ofNullable(emptyToNull(dto.getChecksum())).ifPresent(issueBuilder::setHash);
  203. completeIssueLocations(dto, issueBuilder, data);
  204. issueBuilder.setAuthor(nullToEmpty(dto.getAuthorLogin()));
  205. ofNullable(dto.getIssueCreationDate()).map(DateUtils::formatDateTime).ifPresent(issueBuilder::setCreationDate);
  206. ofNullable(dto.getIssueUpdateDate()).map(DateUtils::formatDateTime).ifPresent(issueBuilder::setUpdateDate);
  207. ofNullable(dto.getIssueCloseDate()).map(DateUtils::formatDateTime).ifPresent(issueBuilder::setCloseDate);
  208. Optional.of(dto.isQuickFixAvailable())
  209. .ifPresentOrElse(issueBuilder::setQuickFixAvailable, () -> issueBuilder.setQuickFixAvailable(false));
  210. issueBuilder.setScope(UNIT_TEST_FILE.equals(component.qualifier()) ? IssueScope.TEST.name() : IssueScope.MAIN.name());
  211. }
  212. private static void addAdditionalFieldsToIssueBuilder(Collection<SearchAdditionalField> fields, SearchResponseData data, IssueDto dto, Issue.Builder issueBuilder) {
  213. if (fields.contains(ACTIONS)) {
  214. issueBuilder.setActions(createIssueActions(data, dto));
  215. }
  216. if (fields.contains(TRANSITIONS)) {
  217. issueBuilder.setTransitions(createIssueTransition(data, dto));
  218. }
  219. if (fields.contains(COMMENTS)) {
  220. issueBuilder.setComments(createIssueComments(data, dto));
  221. }
  222. if (fields.contains(RULE_DESCRIPTION_CONTEXT_KEY)) {
  223. dto.getOptionalRuleDescriptionContextKey().ifPresent(issueBuilder::setRuleDescriptionContextKey);
  224. }
  225. }
  226. private static String engineNameFrom(RuleKey ruleKey) {
  227. checkState(ruleKey.repository().startsWith(EXTERNAL_RULE_REPO_PREFIX));
  228. return ruleKey.repository().replace(EXTERNAL_RULE_REPO_PREFIX, "");
  229. }
  230. private void completeIssueLocations(IssueDto dto, Issue.Builder issueBuilder, SearchResponseData data) {
  231. DbIssues.Locations locations = dto.parseLocations();
  232. if (locations == null) {
  233. return;
  234. }
  235. textRangeFormatter.formatTextRange(locations, issueBuilder::setTextRange);
  236. issueBuilder.addAllFlows(textRangeFormatter.formatFlows(locations, issueBuilder.getComponent(), data.getComponentsByUuid()));
  237. }
  238. private static Transitions createIssueTransition(SearchResponseData data, IssueDto dto) {
  239. Transitions.Builder wsTransitions = Transitions.newBuilder();
  240. List<Transition> transitions = data.getTransitionsForIssueKey(dto.getKey());
  241. if (transitions != null) {
  242. for (Transition transition : transitions) {
  243. wsTransitions.addTransitions(transition.key());
  244. }
  245. }
  246. return wsTransitions.build();
  247. }
  248. private static Actions createIssueActions(SearchResponseData data, IssueDto dto) {
  249. Actions.Builder wsActions = Actions.newBuilder();
  250. List<String> actions = data.getActionsForIssueKey(dto.getKey());
  251. if (actions != null) {
  252. wsActions.addAllActions(actions);
  253. }
  254. return wsActions.build();
  255. }
  256. private static Comments createIssueComments(SearchResponseData data, IssueDto dto) {
  257. Comments.Builder wsComments = Comments.newBuilder();
  258. List<IssueChangeDto> comments = data.getCommentsForIssueKey(dto.getKey());
  259. if (comments != null) {
  260. Comment.Builder wsComment = Comment.newBuilder();
  261. for (IssueChangeDto comment : comments) {
  262. String markdown = comment.getChangeData();
  263. wsComment
  264. .clear()
  265. .setKey(comment.getKey())
  266. .setUpdatable(data.isUpdatableComment(comment.getKey()))
  267. .setCreatedAt(DateUtils.formatDateTime(new Date(comment.getIssueChangeCreationDate())));
  268. ofNullable(data.getUserByUuid(comment.getUserUuid())).ifPresent(user -> wsComment.setLogin(user.getLogin()));
  269. if (markdown != null) {
  270. wsComment
  271. .setHtmlText(Markdown.convertToHtml(markdown))
  272. .setMarkdown(markdown);
  273. }
  274. wsComments.addComments(wsComment);
  275. }
  276. }
  277. return wsComments.build();
  278. }
  279. private Common.Rules.Builder formatRules(SearchResponseData data) {
  280. Common.Rules.Builder wsRules = Common.Rules.newBuilder();
  281. List<RuleDto> rules = firstNonNull(data.getRules(), emptyList());
  282. for (RuleDto rule : rules) {
  283. wsRules.addRules(formatRule(rule));
  284. }
  285. return wsRules;
  286. }
  287. private Common.Rule.Builder formatRule(RuleDto rule) {
  288. Common.Rule.Builder builder = Common.Rule.newBuilder()
  289. .setKey(rule.getKey().toString())
  290. .setName(nullToEmpty(rule.getName()))
  291. .setStatus(Common.RuleStatus.valueOf(rule.getStatus().name()));
  292. builder.setLang(nullToEmpty(rule.getLanguage()));
  293. Language lang = languages.get(rule.getLanguage());
  294. if (lang != null) {
  295. builder.setLangName(lang.getName());
  296. }
  297. return builder;
  298. }
  299. private static List<Issues.Component> formatComponents(SearchResponseData data) {
  300. Collection<ComponentDto> components = data.getComponents();
  301. List<Issues.Component> result = new ArrayList<>();
  302. for (ComponentDto dto : components) {
  303. Component.Builder builder = Component.newBuilder()
  304. .setKey(dto.getKey())
  305. .setQualifier(dto.qualifier())
  306. .setName(nullToEmpty(dto.name()))
  307. .setLongName(nullToEmpty(dto.longName()))
  308. .setEnabled(dto.isEnabled());
  309. setBranchOrPr(dto, builder, data);
  310. ofNullable(emptyToNull(dto.path())).ifPresent(builder::setPath);
  311. result.add(builder.build());
  312. }
  313. return result;
  314. }
  315. private static void setBranchOrPr(ComponentDto componentDto, Component.Builder builder, SearchResponseData data) {
  316. String branchUuid = componentDto.getCopyComponentUuid() != null ? componentDto.getCopyComponentUuid() : componentDto.branchUuid();
  317. BranchDto branchDto = data.getBranch(branchUuid);
  318. if (branchDto.isMain()) {
  319. return;
  320. }
  321. if (branchDto.getBranchType() == BranchType.BRANCH) {
  322. builder.setBranch(branchDto.getKey());
  323. } else if (branchDto.getBranchType() == BranchType.PULL_REQUEST) {
  324. builder.setPullRequest(branchDto.getKey());
  325. }
  326. }
  327. private static void setBranchOrPr(ComponentDto componentDto, Issue.Builder builder, SearchResponseData data) {
  328. String branchUuid = componentDto.getCopyComponentUuid() != null ? componentDto.getCopyComponentUuid() : componentDto.branchUuid();
  329. BranchDto branchDto = data.getBranch(branchUuid);
  330. if (branchDto.isMain()) {
  331. return;
  332. }
  333. if (branchDto.getBranchType() == BranchType.BRANCH) {
  334. builder.setBranch(branchDto.getKey());
  335. } else if (branchDto.getBranchType() == BranchType.PULL_REQUEST) {
  336. builder.setPullRequest(branchDto.getKey());
  337. }
  338. }
  339. private Users.Builder formatUsers(SearchResponseData data) {
  340. Users.Builder wsUsers = Users.newBuilder();
  341. List<UserDto> users = data.getUsers();
  342. if (users != null) {
  343. User.Builder builder = User.newBuilder();
  344. for (UserDto user : users) {
  345. wsUsers.addUsers(userFormatter.formatUser(builder, user));
  346. }
  347. }
  348. return wsUsers;
  349. }
  350. private Issues.Languages.Builder formatLanguages() {
  351. Issues.Languages.Builder wsLangs = Issues.Languages.newBuilder();
  352. Issues.Language.Builder wsLang = Issues.Language.newBuilder();
  353. for (Language lang : languages.all()) {
  354. wsLang
  355. .clear()
  356. .setKey(lang.getKey())
  357. .setName(lang.getName());
  358. wsLangs.addLanguages(wsLang);
  359. }
  360. return wsLangs;
  361. }
  362. private static void formatFacets(SearchResponseData data, Facets facets, SearchWsResponse.Builder wsSearch) {
  363. Common.Facets.Builder wsFacets = Common.Facets.newBuilder();
  364. SearchAction.SUPPORTED_FACETS.stream()
  365. .filter(f -> !f.equals(FACET_PROJECTS))
  366. .filter(f -> !f.equals(FACET_ASSIGNED_TO_ME))
  367. .filter(f -> !f.equals(PARAM_ASSIGNEES))
  368. .filter(f -> !f.equals(PARAM_RULES))
  369. .forEach(f -> computeStandardFacet(wsFacets, facets, f));
  370. computeAssigneesFacet(wsFacets, facets, data);
  371. computeAssignedToMeFacet(wsFacets, facets, data);
  372. computeRulesFacet(wsFacets, facets, data);
  373. computeProjectsFacet(wsFacets, facets, data);
  374. wsSearch.setFacets(wsFacets.build());
  375. }
  376. private static void computeStandardFacet(Common.Facets.Builder wsFacets, Facets facets, String facetKey) {
  377. LinkedHashMap<String, Long> facet = facets.get(facetKey);
  378. if (facet == null) {
  379. return;
  380. }
  381. Common.Facet.Builder wsFacet = wsFacets.addFacetsBuilder();
  382. wsFacet.setProperty(facetKey);
  383. facet.forEach((value, count) -> wsFacet.addValuesBuilder()
  384. .setVal(value)
  385. .setCount(count)
  386. .build());
  387. wsFacet.build();
  388. }
  389. private static void computeAssigneesFacet(Common.Facets.Builder wsFacets, Facets facets, SearchResponseData data) {
  390. LinkedHashMap<String, Long> facet = facets.get(PARAM_ASSIGNEES);
  391. if (facet == null) {
  392. return;
  393. }
  394. Common.Facet.Builder wsFacet = wsFacets.addFacetsBuilder();
  395. wsFacet.setProperty(PARAM_ASSIGNEES);
  396. facet
  397. .forEach((userUuid, count) -> {
  398. UserDto user = data.getUserByUuid(userUuid);
  399. wsFacet.addValuesBuilder()
  400. .setVal(user == null ? "" : user.getLogin())
  401. .setCount(count)
  402. .build();
  403. });
  404. wsFacet.build();
  405. }
  406. private static void computeAssignedToMeFacet(Common.Facets.Builder wsFacets, Facets facets, SearchResponseData data) {
  407. LinkedHashMap<String, Long> facet = facets.get(FACET_ASSIGNED_TO_ME);
  408. if (facet == null) {
  409. return;
  410. }
  411. Map.Entry<String, Long> entry = facet.entrySet().iterator().next();
  412. UserDto user = data.getUserByUuid(entry.getKey());
  413. checkState(user != null, "User with uuid '%s' has not been found", entry.getKey());
  414. Common.Facet.Builder wsFacet = wsFacets.addFacetsBuilder();
  415. wsFacet.setProperty(FACET_ASSIGNED_TO_ME);
  416. wsFacet.addValuesBuilder()
  417. .setVal(user.getLogin())
  418. .setCount(entry.getValue())
  419. .build();
  420. }
  421. private static void computeRulesFacet(Common.Facets.Builder wsFacets, Facets facets, SearchResponseData data) {
  422. LinkedHashMap<String, Long> facet = facets.get(PARAM_RULES);
  423. if (facet == null) {
  424. return;
  425. }
  426. Map<String, RuleKey> ruleUuidsByRuleKeys = data.getRules().stream().collect(Collectors.toMap(RuleDto::getUuid, RuleDto::getKey));
  427. Common.Facet.Builder wsFacet = wsFacets.addFacetsBuilder();
  428. wsFacet.setProperty(PARAM_RULES);
  429. facet.forEach((ruleUuid, count) -> wsFacet.addValuesBuilder()
  430. .setVal(ruleUuidsByRuleKeys.get(ruleUuid).toString())
  431. .setCount(count)
  432. .build());
  433. wsFacet.build();
  434. }
  435. private static void computeProjectsFacet(Common.Facets.Builder wsFacets, Facets facets, SearchResponseData datas) {
  436. LinkedHashMap<String, Long> facet = facets.get(FACET_PROJECTS);
  437. if (facet == null) {
  438. return;
  439. }
  440. Common.Facet.Builder wsFacet = wsFacets.addFacetsBuilder();
  441. wsFacet.setProperty(FACET_PROJECTS);
  442. facet.forEach((uuid, count) -> {
  443. ProjectDto project = datas.getProject(uuid);
  444. requireNonNull(project, format("Project has not been found for uuid '%s'", uuid));
  445. wsFacet.addValuesBuilder()
  446. .setVal(project.getKey())
  447. .setCount(count)
  448. .build();
  449. });
  450. wsFacet.build();
  451. }
  452. }