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.

SearchAction.java 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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 com.google.common.collect.Lists;
  22. import java.util.ArrayList;
  23. import java.util.Arrays;
  24. import java.util.Collection;
  25. import java.util.EnumSet;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Optional;
  29. import java.util.Set;
  30. import java.util.stream.Collectors;
  31. import javax.annotation.Nullable;
  32. import org.apache.lucene.search.TotalHits;
  33. import org.elasticsearch.action.search.SearchResponse;
  34. import org.elasticsearch.search.SearchHit;
  35. import org.sonar.api.issue.DefaultTransitions;
  36. import org.sonar.api.issue.impact.SoftwareQuality;
  37. import org.sonar.api.rule.Severity;
  38. import org.sonar.api.rules.CleanCodeAttributeCategory;
  39. import org.sonar.api.rules.RuleType;
  40. import org.sonar.api.server.ws.Change;
  41. import org.sonar.api.server.ws.Request;
  42. import org.sonar.api.server.ws.Response;
  43. import org.sonar.api.server.ws.WebService;
  44. import org.sonar.api.server.ws.WebService.Param;
  45. import org.sonar.api.utils.Paging;
  46. import org.sonar.api.utils.System2;
  47. import org.sonar.core.issue.status.IssueStatus;
  48. import org.sonar.db.DbClient;
  49. import org.sonar.db.DbSession;
  50. import org.sonar.db.user.UserDto;
  51. import org.sonar.server.es.Facets;
  52. import org.sonar.server.es.SearchOptions;
  53. import org.sonar.server.issue.SearchRequest;
  54. import org.sonar.server.issue.index.IssueIndex;
  55. import org.sonar.server.issue.index.IssueIndexSyncProgressChecker;
  56. import org.sonar.server.issue.index.IssueQuery;
  57. import org.sonar.server.issue.index.IssueQueryFactory;
  58. import org.sonar.server.issue.index.IssueScope;
  59. import org.sonar.server.security.SecurityStandards.SQCategory;
  60. import org.sonar.server.user.UserSession;
  61. import org.sonarqube.ws.Issues.SearchWsResponse;
  62. import static com.google.common.base.MoreObjects.firstNonNull;
  63. import static com.google.common.base.Preconditions.checkArgument;
  64. import static com.google.common.collect.Iterables.concat;
  65. import static com.google.common.collect.Sets.newHashSet;
  66. import static java.lang.String.format;
  67. import static java.util.Collections.emptyList;
  68. import static java.util.Collections.singletonList;
  69. import static java.util.Optional.ofNullable;
  70. import static org.sonar.api.issue.Issue.RESOLUTIONS;
  71. import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
  72. import static org.sonar.api.issue.Issue.RESOLUTION_REMOVED;
  73. import static org.sonar.api.issue.Issue.STATUS_IN_REVIEW;
  74. import static org.sonar.api.issue.Issue.STATUS_OPEN;
  75. import static org.sonar.api.issue.Issue.STATUS_REOPENED;
  76. import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
  77. import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
  78. import static org.sonar.api.server.ws.WebService.Param.FACETS;
  79. import static org.sonar.api.utils.Paging.forPageIndex;
  80. import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
  81. import static org.sonar.server.issue.index.IssueIndex.FACET_ASSIGNED_TO_ME;
  82. import static org.sonar.server.issue.index.IssueIndex.FACET_PROJECTS;
  83. import static org.sonar.server.issue.index.IssueQueryFactory.ISSUE_STATUSES;
  84. import static org.sonar.server.issue.index.IssueQueryFactory.UNKNOWN;
  85. import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION;
  86. import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES;
  87. import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE;
  88. import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD;
  89. import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
  90. import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
  91. import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
  92. import static org.sonar.server.ws.WsUtils.writeProtobuf;
  93. import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SEARCH;
  94. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ADDITIONAL_FIELDS;
  95. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASC;
  96. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNED;
  97. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
  98. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHOR;
  99. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_BRANCH;
  100. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES;
  101. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CODE_VARIANTS;
  102. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENTS;
  103. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
  104. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AFTER;
  105. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT;
  106. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_BEFORE;
  107. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_IN_LAST;
  108. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CWE;
  109. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_DIRECTORIES;
  110. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILES;
  111. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FIXED_IN_PULL_REQUEST;
  112. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SEVERITIES;
  113. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SOFTWARE_QUALITIES;
  114. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IN_NEW_CODE_PERIOD;
  115. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUES;
  116. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUE_STATUSES;
  117. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_LANGUAGES;
  118. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ON_COMPONENT_ONLY;
  119. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_ASVS_40;
  120. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_ASVS_LEVEL;
  121. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10;
  122. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10_2021;
  123. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PCI_DSS_32;
  124. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PCI_DSS_40;
  125. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS;
  126. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PULL_REQUEST;
  127. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS;
  128. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLVED;
  129. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
  130. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
  131. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES;
  132. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES;
  133. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY;
  134. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES;
  135. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS;
  136. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TIMEZONE;
  137. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TYPES;
  138. public class SearchAction implements IssuesWsAction {
  139. private static final String LOGIN_MYSELF = "__me__";
  140. private static final Set<String> ISSUE_SCOPES = Arrays.stream(IssueScope.values()).map(Enum::name).collect(Collectors.toSet());
  141. private static final EnumSet<RuleType> ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS =
  142. EnumSet.complementOf(EnumSet.of(RuleType.SECURITY_HOTSPOT));
  143. static final List<String> SUPPORTED_FACETS = List.of(
  144. FACET_PROJECTS,
  145. PARAM_FILES,
  146. FACET_ASSIGNED_TO_ME,
  147. PARAM_SEVERITIES,
  148. PARAM_STATUSES,
  149. PARAM_RESOLUTIONS,
  150. PARAM_RULES,
  151. PARAM_ASSIGNEES,
  152. PARAM_AUTHOR,
  153. PARAM_DIRECTORIES,
  154. PARAM_SCOPES,
  155. PARAM_LANGUAGES,
  156. PARAM_TAGS,
  157. PARAM_TYPES,
  158. PARAM_PCI_DSS_32,
  159. PARAM_PCI_DSS_40,
  160. PARAM_OWASP_ASVS_40,
  161. PARAM_OWASP_TOP_10,
  162. PARAM_OWASP_TOP_10_2021,
  163. PARAM_SANS_TOP_25,
  164. PARAM_CWE,
  165. PARAM_CREATED_AT,
  166. PARAM_SONARSOURCE_SECURITY,
  167. PARAM_CODE_VARIANTS,
  168. PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES,
  169. PARAM_IMPACT_SOFTWARE_QUALITIES,
  170. PARAM_IMPACT_SEVERITIES,
  171. PARAM_ISSUE_STATUSES);
  172. private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of " +
  173. "the componentKeys parameter. ";
  174. private static final String NEW_FACET_ADDED_MESSAGE = "Facet '%s' has been added";
  175. private static final String NEW_PARAM_ADDED_MESSAGE = "Param '%s' has been added";
  176. private static final Set<String> FACETS_REQUIRING_PROJECT = newHashSet(PARAM_FILES, PARAM_DIRECTORIES);
  177. private final UserSession userSession;
  178. private final IssueIndex issueIndex;
  179. private final IssueQueryFactory issueQueryFactory;
  180. private final IssueIndexSyncProgressChecker issueIndexSyncProgressChecker;
  181. private final SearchResponseLoader searchResponseLoader;
  182. private final SearchResponseFormat searchResponseFormat;
  183. private final System2 system2;
  184. private final DbClient dbClient;
  185. public SearchAction(UserSession userSession, IssueIndex issueIndex, IssueQueryFactory issueQueryFactory,
  186. IssueIndexSyncProgressChecker issueIndexSyncProgressChecker,
  187. SearchResponseLoader searchResponseLoader, SearchResponseFormat searchResponseFormat, System2 system2, DbClient dbClient) {
  188. this.userSession = userSession;
  189. this.issueIndex = issueIndex;
  190. this.issueQueryFactory = issueQueryFactory;
  191. this.issueIndexSyncProgressChecker = issueIndexSyncProgressChecker;
  192. this.searchResponseLoader = searchResponseLoader;
  193. this.searchResponseFormat = searchResponseFormat;
  194. this.system2 = system2;
  195. this.dbClient = dbClient;
  196. }
  197. @Override
  198. public void define(WebService.NewController controller) {
  199. WebService.NewAction action = controller
  200. .createAction(ACTION_SEARCH)
  201. .setHandler(this)
  202. .setDescription("Search for issues.<br>Requires the 'Browse' permission on the specified project(s). <br>"
  203. + "For applications, it also requires 'Browse' permission on its child projects."
  204. + "<br/>When issue indexation is in progress returns 503 service unavailable HTTP code.")
  205. .setSince("3.6")
  206. .setChangelog(
  207. new Change("10.4", "Added new param '%s'".formatted(PARAM_FIXED_IN_PULL_REQUEST)),
  208. new Change("10.4",
  209. "Value '%s' for 'transition' response field is deprecated, use '%s' instead".formatted(DefaultTransitions.WONT_FIX,
  210. DefaultTransitions.ACCEPT)),
  211. new Change("10.4", "Possible value '%s' for 'transition' response field has been added".formatted(DefaultTransitions.ACCEPT)),
  212. new Change("10.4", format(NEW_PARAM_ADDED_MESSAGE, PARAM_ISSUE_STATUSES)),
  213. new Change("10.4", format("Parameters '%s' and '%s' are deprecated in favor of '%s'.", PARAM_RESOLUTIONS, PARAM_STATUSES, PARAM_ISSUE_STATUSES)),
  214. new Change("10.4", format("Parameters '%s' and '%s' are deprecated, use '%s' and '%s' instead.", PARAM_SEVERITIES, PARAM_TYPES,
  215. PARAM_IMPACT_SEVERITIES, PARAM_IMPACT_SOFTWARE_QUALITIES)),
  216. new Change("10.4", format(NEW_FACET_ADDED_MESSAGE, PARAM_ISSUE_STATUSES)),
  217. new Change("10.4", format("Facets '%s' and '%s' are deprecated in favor of '%s'", PARAM_RESOLUTIONS, PARAM_STATUSES, PARAM_ISSUE_STATUSES)),
  218. new Change("10.4", "Response fields 'severity' and 'type' are deprecated, use 'impacts' instead."),
  219. new Change("10.4", "Response field 'issueStatus' added"),
  220. new Change("10.4", "Response fields 'status' and 'resolutions' are deprecated, in favor of 'issueStatus'"),
  221. new Change("10.4", format("Possible value '%s' for 'issueStatus' field is deprecated.", IssueStatus.CONFIRMED)),
  222. new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
  223. new Change("10.2", format(NEW_PARAM_ADDED_MESSAGE, PARAM_IMPACT_SOFTWARE_QUALITIES)),
  224. new Change("10.2", format(NEW_PARAM_ADDED_MESSAGE, PARAM_IMPACT_SEVERITIES)),
  225. new Change("10.2", format(NEW_PARAM_ADDED_MESSAGE, PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES)),
  226. new Change("10.2", format(NEW_FACET_ADDED_MESSAGE, PARAM_IMPACT_SOFTWARE_QUALITIES)),
  227. new Change("10.2", format(NEW_FACET_ADDED_MESSAGE, PARAM_IMPACT_SEVERITIES)),
  228. new Change("10.2", format(NEW_FACET_ADDED_MESSAGE, PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES)),
  229. new Change("10.2", format("Parameter '%s' renamed to '%s'", PARAM_COMPONENT_KEYS, PARAM_COMPONENTS)),
  230. new Change("10.1", "Add the 'codeVariants' parameter, facet and response field"),
  231. new Change("10.0", "Parameter 'sansTop25' is deprecated"),
  232. new Change("10.0", "The value 'sansTop25' for the parameter 'facets' has been deprecated"),
  233. new Change("10.0", format("Deprecated value 'ASSIGNEE' in parameter '%s' is dropped", Param.SORT)),
  234. new Change("10.0", format("Parameter 'sinceLeakPeriod' is removed, please use '%s' instead", PARAM_IN_NEW_CODE_PERIOD)),
  235. new Change("9.8", "Add message formatting to issue and locations response"),
  236. new Change("9.8", "response fields 'total', 's', 'ps' have been deprecated, please use 'paging' object instead"),
  237. new Change("9.7", "Issues flows in the response may contain a description and a type"),
  238. new Change("9.6", "Response field 'fromHotspot' dropped."),
  239. new Change("9.6", "Added facets 'pciDss-3.2' and 'pciDss-4.0"),
  240. new Change("9.6", "Added parameters 'pciDss-3.2' and 'pciDss-4.0"),
  241. new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
  242. new Change("9.6", "New possible value for 'additionalFields' parameter: 'ruleDescriptionContextKey'"),
  243. new Change("9.6", "Facet 'moduleUuids' is dropped."),
  244. new Change("9.4", format("Parameter 'sinceLeakPeriod' is deprecated, please use '%s' instead", PARAM_IN_NEW_CODE_PERIOD)),
  245. new Change("9.2", "Response field 'quickFixAvailable' added"),
  246. new Change("9.1", "Deprecated parameters 'authors', 'facetMode' and 'moduleUuids' were dropped"),
  247. new Change("8.6", "Parameter 'timeZone' added"),
  248. new Change("8.5", "Facet 'fileUuids' is dropped in favour of the new facet 'files'" +
  249. "Note that they are not strictly identical, the latter returns the file paths."),
  250. new Change("8.5", "Internal parameter 'fileUuids' has been dropped"),
  251. new Change("8.4", "parameters 'componentUuids', 'projectKeys' has been dropped."),
  252. new Change("8.2", "'REVIEWED', 'TO_REVIEW' status param values are no longer supported"),
  253. new Change("8.2", "Security hotspots are no longer returned as type 'SECURITY_HOTSPOT' is not supported anymore, use dedicated " +
  254. "api/hotspots"),
  255. new Change("8.2", "response field 'fromHotspot' has been deprecated and is no more populated"),
  256. new Change("8.2", "Status 'IN_REVIEW' for Security Hotspots has been deprecated"),
  257. new Change("7.8", format("added new Security Hotspots statuses : %s, %s and %s", STATUS_TO_REVIEW, STATUS_IN_REVIEW,
  258. STATUS_REVIEWED)),
  259. new Change("7.8", "Security hotspots are returned by default"),
  260. new Change("7.7", format("Value 'authors' in parameter '%s' is deprecated, please use '%s' instead", FACETS, PARAM_AUTHOR)),
  261. new Change("7.6", format("The use of module keys in parameter '%s' is deprecated", PARAM_COMPONENT_KEYS)),
  262. new Change("7.4", "The facet 'projectUuids' is dropped in favour of the new facet 'projects'. " +
  263. "Note that they are not strictly identical, the latter returns the project keys."),
  264. new Change("7.4", "Parameter 'facetMode' does not accept anymore deprecated value 'debt'"),
  265. new Change("7.3", "response field 'fromHotspot' added to issues that are security hotspots"),
  266. new Change("7.3", "added facets 'sansTop25', 'owaspTop10' and 'cwe'"),
  267. new Change("7.2", "response field 'externalRuleEngine' added to issues that have been imported from an external rule engine"),
  268. new Change("7.2", format("value 'ASSIGNEE' in parameter '%s' is deprecated, it won't have any effect", Param.SORT)),
  269. new Change("6.5", "parameters 'projects', 'projectUuids', 'moduleUuids', 'directories', 'fileUuids' are marked as internal"),
  270. new Change("6.3", "response field 'email' is renamed 'avatar'"),
  271. new Change("5.5", "response fields 'reporter' and 'actionPlan' are removed (drop of action plan and manual issue features)"),
  272. new Change("5.5", "parameters 'reporters', 'actionPlans' and 'planned' are dropped and therefore ignored (drop of action plan and" +
  273. " manual issue features)"),
  274. new Change("5.5", "response field 'debt' is renamed 'effort'"))
  275. .setResponseExample(getClass().getResource("search-example.json"));
  276. action.addPagingParams(100, MAX_PAGE_SIZE);
  277. action.createParam(FACETS)
  278. .setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
  279. .setPossibleValues(SUPPORTED_FACETS);
  280. action.addSortParams(IssueQuery.SORTS, null, true);
  281. action.createParam(PARAM_ADDITIONAL_FIELDS)
  282. .setSince("5.2")
  283. .setDescription("Comma-separated list of the optional fields to be returned in response. Action plans are dropped in 5.5, it is not" +
  284. " returned in the response.")
  285. .setPossibleValues(SearchAdditionalField.possibleValues());
  286. addComponentRelatedParams(action);
  287. action.createParam(PARAM_ISSUES)
  288. .setDescription("Comma-separated list of issue keys")
  289. .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
  290. action.createParam(PARAM_SEVERITIES)
  291. .setDescription("Comma-separated list of severities")
  292. .setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL)
  293. .setPossibleValues(Severity.ALL)
  294. .setDeprecatedSince("10.4");
  295. action.createParam(PARAM_IMPACT_SOFTWARE_QUALITIES)
  296. .setSince("10.2")
  297. .setDescription("Comma-separated list of Software Qualities")
  298. .setExampleValue(SoftwareQuality.MAINTAINABILITY + "," + SoftwareQuality.RELIABILITY)
  299. .setPossibleValues(SoftwareQuality.values());
  300. action.createParam(PARAM_IMPACT_SEVERITIES)
  301. .setSince("10.2")
  302. .setDescription("Comma-separated list of Software Quality Severities")
  303. .setExampleValue(org.sonar.api.issue.impact.Severity.HIGH + "," + org.sonar.api.issue.impact.Severity.MEDIUM)
  304. .setPossibleValues(org.sonar.api.issue.impact.Severity.values());
  305. action.createParam(PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES)
  306. .setSince("10.2")
  307. .setDescription("Comma-separated list of Clean Code Attribute Categories")
  308. .setExampleValue(CleanCodeAttributeCategory.ADAPTABLE + "," + CleanCodeAttributeCategory.INTENTIONAL)
  309. .setPossibleValues(CleanCodeAttributeCategory.values());
  310. action.createParam(PARAM_STATUSES)
  311. .setDescription("Comma-separated list of statuses")
  312. .setExampleValue(STATUS_OPEN + "," + STATUS_REOPENED)
  313. .setPossibleValues(ISSUE_STATUSES)
  314. .setDeprecatedSince("10.4");
  315. action.createParam(PARAM_RESOLUTIONS)
  316. .setDescription("Comma-separated list of resolutions")
  317. .setExampleValue(RESOLUTION_FIXED + "," + RESOLUTION_REMOVED)
  318. .setPossibleValues(RESOLUTIONS)
  319. .setDeprecatedSince("10.4");
  320. action.createParam(PARAM_RESOLVED)
  321. .setDescription("To match resolved or unresolved issues")
  322. .setBooleanPossibleValues();
  323. action.createParam(PARAM_RULES)
  324. .setDescription("Comma-separated list of coding rule keys. Format is &lt;repository&gt;:&lt;rule&gt;")
  325. .setExampleValue("java:S1144");
  326. action.createParam(PARAM_TAGS)
  327. .setDescription("Comma-separated list of tags.")
  328. .setExampleValue("security,convention");
  329. action.createParam(PARAM_TYPES)
  330. .setDescription("Comma-separated list of types.")
  331. .setSince("5.5")
  332. .setDeprecatedSince("10.4")
  333. .setPossibleValues(ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS)
  334. .setExampleValue(format("%s,%s", RuleType.CODE_SMELL, RuleType.BUG));
  335. action.createParam(PARAM_OWASP_ASVS_LEVEL)
  336. .setDescription("Level of OWASP ASVS categories.")
  337. .setSince("9.7")
  338. .setPossibleValues(1, 2, 3);
  339. action.createParam(PARAM_PCI_DSS_32)
  340. .setDescription("Comma-separated list of PCI DSS v3.2 categories.")
  341. .setSince("9.6")
  342. .setExampleValue("4,6.5.8,10.1");
  343. action.createParam(PARAM_PCI_DSS_40)
  344. .setDescription("Comma-separated list of PCI DSS v4.0 categories.")
  345. .setSince("9.6")
  346. .setExampleValue("4,6.5.8,10.1");
  347. action.createParam(PARAM_OWASP_ASVS_40)
  348. .setDescription("Comma-separated list of OWASP ASVS v4.0 categories.")
  349. .setSince("9.7")
  350. .setExampleValue("6,10.1.1");
  351. action.createParam(PARAM_OWASP_TOP_10)
  352. .setDescription("Comma-separated list of OWASP Top 10 2017 lowercase categories.")
  353. .setSince("7.3")
  354. .setPossibleValues("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10");
  355. action.createParam(PARAM_OWASP_TOP_10_2021)
  356. .setDescription("Comma-separated list of OWASP Top 10 2021 lowercase categories.")
  357. .setSince("9.4")
  358. .setPossibleValues("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10");
  359. action.createParam(PARAM_SANS_TOP_25)
  360. .setDescription("Comma-separated list of SANS Top 25 categories.")
  361. .setDeprecatedSince("10.0")
  362. .setSince("7.3")
  363. .setPossibleValues(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES);
  364. action.createParam(PARAM_CWE)
  365. .setDescription("Comma-separated list of CWE identifiers. Use '" + UNKNOWN_STANDARD + "' to select issues not associated to any CWE.")
  366. .setExampleValue("12,125," + UNKNOWN_STANDARD);
  367. action.createParam(PARAM_SONARSOURCE_SECURITY)
  368. .setDescription("Comma-separated list of SonarSource security categories. Use '" + SQCategory.OTHERS.getKey() + "' to select issues" +
  369. " not associated" +
  370. " with any category")
  371. .setSince("7.8")
  372. .setPossibleValues(Arrays.stream(SQCategory.values()).map(SQCategory::getKey).toList());
  373. action.createParam(PARAM_AUTHOR)
  374. .setDescription("SCM accounts. To set several values, the parameter must be called once for each value.")
  375. .setExampleValue("author=torvalds@linux-foundation.org&author=linux@fondation.org");
  376. action.createParam(PARAM_ASSIGNEES)
  377. .setDescription("Comma-separated list of assignee logins. The value '__me__' can be used as a placeholder for user who performs the" +
  378. " request")
  379. .setExampleValue("admin,usera,__me__");
  380. action.createParam(PARAM_ASSIGNED)
  381. .setDescription("To retrieve assigned or unassigned issues")
  382. .setBooleanPossibleValues();
  383. action.createParam(PARAM_SCOPES)
  384. .setDescription("Comma-separated list of scopes. Available since 8.5")
  385. .setPossibleValues(IssueScope.MAIN.name(), IssueScope.TEST.name())
  386. .setExampleValue(format("%s,%s", IssueScope.MAIN.name(), IssueScope.TEST.name()));
  387. action.createParam(PARAM_LANGUAGES)
  388. .setDescription("Comma-separated list of languages. Available since 4.4")
  389. .setExampleValue("java,js");
  390. action.createParam(PARAM_CREATED_AT)
  391. .setDescription("Datetime to retrieve issues created during a specific analysis")
  392. .setExampleValue("2017-10-19T13:00:00+0200");
  393. action.createParam(PARAM_CREATED_AFTER)
  394. .setDescription("To retrieve issues created after the given date (inclusive). <br>" +
  395. "Either a date (use '" + PARAM_TIMEZONE + "' attribute or it will default to server timezone) or datetime can be provided. <br>" +
  396. "If this parameter is set, createdInLast must not be set")
  397. .setExampleValue("2017-10-19 or 2017-10-19T13:00:00+0200");
  398. action.createParam(PARAM_CREATED_BEFORE)
  399. .setDescription("To retrieve issues created before the given date (exclusive). <br>" +
  400. "Either a date (use '" + PARAM_TIMEZONE + "' attribute or it will default to server timezone) or datetime can be provided.")
  401. .setExampleValue("2017-10-19 or 2017-10-19T13:00:00+0200");
  402. action.createParam(PARAM_CREATED_IN_LAST)
  403. .setDescription("To retrieve issues created during a time span before the current time (exclusive). " +
  404. "Accepted units are 'y' for year, 'm' for month, 'w' for week and 'd' for day. " +
  405. "If this parameter is set, createdAfter must not be set")
  406. .setExampleValue("1m2w (1 month 2 weeks)");
  407. action.createParam(PARAM_IN_NEW_CODE_PERIOD)
  408. .setDescription("To retrieve issues created in the new code period.<br>" +
  409. "If this parameter is set to a truthy value, createdAfter must not be set and one component uuid or key must be provided.")
  410. .setBooleanPossibleValues()
  411. .setSince("9.4");
  412. action.createParam(PARAM_TIMEZONE)
  413. .setDescription(
  414. "To resolve dates passed to '" + PARAM_CREATED_AFTER + "' or '" + PARAM_CREATED_BEFORE + "' (does not apply to datetime) and to " +
  415. "compute creation date histogram")
  416. .setRequired(false)
  417. .setExampleValue("'Europe/Paris', 'Z' or '+02:00'")
  418. .setSince("8.6");
  419. action.createParam(PARAM_CODE_VARIANTS)
  420. .setDescription("Comma-separated list of code variants.")
  421. .setExampleValue("windows,linux")
  422. .setSince("10.1");
  423. action.createParam(PARAM_ISSUE_STATUSES)
  424. .setDescription("")
  425. .setPossibleValues(IssueStatus.values())
  426. .setExampleValue("%s,%S".formatted(IssueStatus.ACCEPTED, IssueStatus.FIXED))
  427. .setSince("10.4");
  428. }
  429. private static void addComponentRelatedParams(WebService.NewAction action) {
  430. action.createParam(PARAM_ON_COMPONENT_ONLY)
  431. .setDescription("Return only issues at a component's level, not on its descendants (modules, directories, files, etc). " +
  432. "This parameter is only considered when componentKeys is set.")
  433. .setBooleanPossibleValues()
  434. .setDefaultValue("false");
  435. action.createParam(PARAM_COMPONENTS)
  436. .setDeprecatedKey(PARAM_COMPONENT_KEYS, "10.2")
  437. .setDescription("Comma-separated list of component keys. Retrieve issues associated to a specific list of components (and all its " +
  438. "descendants). " +
  439. "A component can be a portfolio, project, module, directory or file.")
  440. .setExampleValue(KEY_PROJECT_EXAMPLE_001);
  441. action.createParam(PARAM_PROJECTS)
  442. .setDescription("To retrieve issues associated to a specific list of projects (comma-separated list of project keys). " +
  443. INTERNAL_PARAMETER_DISCLAIMER +
  444. "If this parameter is set, projectUuids must not be set.")
  445. .setInternal(true)
  446. .setExampleValue(KEY_PROJECT_EXAMPLE_001);
  447. action.createParam(PARAM_DIRECTORIES)
  448. .setDescription("To retrieve issues associated to a specific list of directories (comma-separated list of directory paths). " +
  449. "This parameter is only meaningful when a module is selected. " +
  450. INTERNAL_PARAMETER_DISCLAIMER)
  451. .setInternal(true)
  452. .setSince("5.1")
  453. .setExampleValue("src/main/java/org/sonar/server/");
  454. action.createParam(PARAM_FILES)
  455. .setDescription("To retrieve issues associated to a specific list of files (comma-separated list of file paths). " +
  456. INTERNAL_PARAMETER_DISCLAIMER)
  457. .setInternal(true)
  458. .setExampleValue("src/main/java/org/sonar/server/Test.java");
  459. action.createParam(PARAM_BRANCH)
  460. .setDescription("Branch key. Not available in the community edition.")
  461. .setExampleValue(KEY_BRANCH_EXAMPLE_001)
  462. .setSince("6.6");
  463. action.createParam(PARAM_PULL_REQUEST)
  464. .setDescription("Pull request id. Not available in the community edition.")
  465. .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
  466. .setSince("7.1");
  467. action.createParam(PARAM_FIXED_IN_PULL_REQUEST)
  468. .setDescription("Pull request id to filter issues that would be fixed in the specified project or branch by the pull request. " +
  469. "Should not be used together with + '" + PARAM_PULL_REQUEST + "'. At least the '" + PARAM_COMPONENTS + "' must be be specified " +
  470. "when this param is used. Not available in the community edition.")
  471. .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
  472. .setSince("10.4");
  473. }
  474. @Override
  475. public final void handle(Request request, Response response) {
  476. try (DbSession dbSession = dbClient.openSession(false)) {
  477. SearchRequest searchRequest = toSearchWsRequest(dbSession, request);
  478. checkIfNeedIssueSync(dbSession, searchRequest);
  479. SearchWsResponse searchWsResponse = doHandle(searchRequest);
  480. writeProtobuf(searchWsResponse, request, response);
  481. }
  482. }
  483. private SearchWsResponse doHandle(SearchRequest request) {
  484. // prepare the Elasticsearch request
  485. SearchOptions options = createSearchOptionsFromRequest(request);
  486. EnumSet<SearchAdditionalField> additionalFields = SearchAdditionalField.getFromRequest(request);
  487. IssueQuery query = issueQueryFactory.create(request);
  488. Set<String> facetsRequiringProjectParameter = options.getFacets().stream()
  489. .filter(FACETS_REQUIRING_PROJECT::contains)
  490. .collect(Collectors.toSet());
  491. checkArgument(facetsRequiringProjectParameter.isEmpty() ||
  492. (!query.projectUuids().isEmpty()), "Facet(s) '%s' require to also filter by project",
  493. String.join(",", facetsRequiringProjectParameter));
  494. // execute request
  495. SearchResponse result = issueIndex.search(query, options);
  496. result.getHits();
  497. List<String> issueKeys = Arrays.stream(result.getHits().getHits())
  498. .map(SearchHit::getId)
  499. .toList();
  500. // load the additional information to be returned in response
  501. SearchResponseLoader.Collector collector = new SearchResponseLoader.Collector(issueKeys);
  502. collectLoggedInUser(collector);
  503. collectRequestParams(collector, request);
  504. Facets facets = new Facets(result, Optional.ofNullable(query.timeZone()).orElse(system2.getDefaultTimeZone().toZoneId()));
  505. if (!options.getFacets().isEmpty()) {
  506. // add missing values to facets. For example if assignee "john" and facet on "assignees" are requested, then
  507. // "john" should always be listed in the facet. If it is not present, then it is added with value zero.
  508. // This is a constraint from webapp UX.
  509. completeFacets(facets, request, query);
  510. collectFacets(collector, facets);
  511. }
  512. SearchResponseData preloadedData = new SearchResponseData();
  513. preloadedData.addRules(List.copyOf(query.rules()));
  514. SearchResponseData data = searchResponseLoader.load(preloadedData, collector, additionalFields, facets);
  515. // FIXME allow long in Paging
  516. Paging paging = forPageIndex(options.getPage()).withPageSize(options.getLimit()).andTotal((int) getTotalHits(result).value);
  517. return searchResponseFormat.formatSearch(additionalFields, data, paging, facets);
  518. }
  519. private static TotalHits getTotalHits(SearchResponse response) {
  520. return ofNullable(response.getHits().getTotalHits()).orElseThrow(() -> new IllegalStateException("Could not get total hits of search " +
  521. "results"));
  522. }
  523. private static SearchOptions createSearchOptionsFromRequest(SearchRequest request) {
  524. SearchOptions options = new SearchOptions();
  525. options.setPage(request.getPage(), request.getPageSize());
  526. List<String> facets = request.getFacets();
  527. if (facets != null && !facets.isEmpty()) {
  528. options.addFacets(facets);
  529. }
  530. return options;
  531. }
  532. private void completeFacets(Facets facets, SearchRequest request, IssueQuery query) {
  533. addMandatoryValuesToFacet(facets, PARAM_SEVERITIES, Severity.ALL);
  534. addMandatoryValuesToFacet(facets, PARAM_STATUSES, ISSUE_STATUSES);
  535. addMandatoryValuesToFacet(facets, PARAM_IMPACT_SOFTWARE_QUALITIES, enumToStringCollection(SoftwareQuality.values()));
  536. addMandatoryValuesToFacet(facets, PARAM_IMPACT_SEVERITIES, enumToStringCollection(org.sonar.api.issue.impact.Severity.values()));
  537. addMandatoryValuesToFacet(facets, PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES, enumToStringCollection(CleanCodeAttributeCategory.values()));
  538. addMandatoryValuesToFacet(facets, PARAM_RESOLUTIONS, concat(singletonList(""), RESOLUTIONS));
  539. addMandatoryValuesToFacet(facets, FACET_PROJECTS, query.projectUuids());
  540. addMandatoryValuesToFacet(facets, PARAM_FILES, query.files());
  541. List<String> assignees = Lists.newArrayList("");
  542. List<String> assigneesFromRequest = request.getAssigneeUuids();
  543. if (assigneesFromRequest != null) {
  544. assignees.addAll(assigneesFromRequest);
  545. assignees.remove(LOGIN_MYSELF);
  546. }
  547. addMandatoryValuesToFacet(facets, PARAM_ASSIGNEES, assignees);
  548. addMandatoryValuesToFacet(facets, FACET_ASSIGNED_TO_ME, singletonList(userSession.getUuid()));
  549. addMandatoryValuesToFacet(facets, PARAM_RULES, query.ruleUuids());
  550. addMandatoryValuesToFacet(facets, PARAM_SCOPES, ISSUE_SCOPES);
  551. addMandatoryValuesToFacet(facets, PARAM_LANGUAGES, request.getLanguages());
  552. addMandatoryValuesToFacet(facets, PARAM_TAGS, request.getTags());
  553. setTypesFacet(facets);
  554. addMandatoryValuesToFacet(facets, PARAM_PCI_DSS_32, request.getPciDss32());
  555. addMandatoryValuesToFacet(facets, PARAM_PCI_DSS_40, request.getPciDss40());
  556. addMandatoryValuesToFacet(facets, PARAM_OWASP_ASVS_40, request.getOwaspAsvs40());
  557. addMandatoryValuesToFacet(facets, PARAM_OWASP_TOP_10, request.getOwaspTop10());
  558. addMandatoryValuesToFacet(facets, PARAM_OWASP_TOP_10_2021, request.getOwaspTop10For2021());
  559. addMandatoryValuesToFacet(facets, PARAM_SANS_TOP_25, request.getSansTop25());
  560. addMandatoryValuesToFacet(facets, PARAM_CWE, request.getCwe());
  561. addMandatoryValuesToFacet(facets, PARAM_SONARSOURCE_SECURITY, request.getSonarsourceSecurity());
  562. addMandatoryValuesToFacet(facets, PARAM_CODE_VARIANTS, request.getCodeVariants());
  563. }
  564. private static Collection<String> enumToStringCollection(Enum<?>... enumValues) {
  565. return Arrays.stream(enumValues).map(Enum::name).toList();
  566. }
  567. private static void setTypesFacet(Facets facets) {
  568. Map<String, Long> typeFacet = facets.get(PARAM_TYPES);
  569. if (typeFacet != null) {
  570. typeFacet.remove(RuleType.SECURITY_HOTSPOT.name());
  571. }
  572. addMandatoryValuesToFacet(facets, PARAM_TYPES, ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS.stream().map(Enum::name).toList());
  573. }
  574. private static void addMandatoryValuesToFacet(Facets facets, String facetName, @Nullable Iterable<String> mandatoryValues) {
  575. Map<String, Long> buckets = facets.get(facetName);
  576. if (buckets != null && mandatoryValues != null) {
  577. for (String mandatoryValue : mandatoryValues) {
  578. buckets.putIfAbsent(mandatoryValue, 0L);
  579. }
  580. }
  581. }
  582. private void collectLoggedInUser(SearchResponseLoader.Collector collector) {
  583. if (userSession.isLoggedIn()) {
  584. collector.addUserUuids(singletonList(userSession.getUuid()));
  585. }
  586. }
  587. private static void collectFacets(SearchResponseLoader.Collector collector, Facets facets) {
  588. collector.addProjectUuids(facets.getBucketKeys(FACET_PROJECTS));
  589. collector.addRuleIds(facets.getBucketKeys(PARAM_RULES));
  590. collector.addUserUuids(facets.getBucketKeys(PARAM_ASSIGNEES));
  591. }
  592. private static void collectRequestParams(SearchResponseLoader.Collector collector, SearchRequest request) {
  593. collector.addUserUuids(request.getAssigneeUuids());
  594. }
  595. private SearchRequest toSearchWsRequest(DbSession dbSession, Request request) {
  596. return new SearchRequest()
  597. .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))
  598. .setAsc(request.mandatoryParamAsBoolean(PARAM_ASC))
  599. .setAssigned(request.paramAsBoolean(PARAM_ASSIGNED))
  600. .setAssigneesUuid(getLogins(dbSession, request.paramAsStrings(PARAM_ASSIGNEES)))
  601. .setAuthors(request.multiParam(PARAM_AUTHOR))
  602. .setComponentKeys(request.paramAsStrings(PARAM_COMPONENTS))
  603. .setCreatedAfter(request.param(PARAM_CREATED_AFTER))
  604. .setCreatedAt(request.param(PARAM_CREATED_AT))
  605. .setCreatedBefore(request.param(PARAM_CREATED_BEFORE))
  606. .setCreatedInLast(request.param(PARAM_CREATED_IN_LAST))
  607. .setDirectories(request.paramAsStrings(PARAM_DIRECTORIES))
  608. .setFacets(request.paramAsStrings(FACETS))
  609. .setFiles(request.paramAsStrings(PARAM_FILES))
  610. .setInNewCodePeriod(request.paramAsBoolean(PARAM_IN_NEW_CODE_PERIOD))
  611. .setIssues(request.paramAsStrings(PARAM_ISSUES))
  612. .setScopes(request.paramAsStrings(PARAM_SCOPES))
  613. .setLanguages(request.paramAsStrings(PARAM_LANGUAGES))
  614. .setOnComponentOnly(request.paramAsBoolean(PARAM_ON_COMPONENT_ONLY))
  615. .setBranch(request.param(PARAM_BRANCH))
  616. .setPullRequest(request.param(PARAM_PULL_REQUEST))
  617. .setPage(request.mandatoryParamAsInt(Param.PAGE))
  618. .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
  619. .setProjectKeys(request.paramAsStrings(PARAM_PROJECTS))
  620. .setResolutions(request.paramAsStrings(PARAM_RESOLUTIONS))
  621. .setResolved(request.paramAsBoolean(PARAM_RESOLVED))
  622. .setRules(request.paramAsStrings(PARAM_RULES))
  623. .setSort(request.param(Param.SORT))
  624. .setSeverities(request.paramAsStrings(PARAM_SEVERITIES))
  625. .setImpactSeverities(request.paramAsStrings(PARAM_IMPACT_SEVERITIES))
  626. .setImpactSoftwareQualities(request.paramAsStrings(PARAM_IMPACT_SOFTWARE_QUALITIES))
  627. .setCleanCodeAttributesCategories(request.paramAsStrings(PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES))
  628. .setStatuses(request.paramAsStrings(PARAM_STATUSES))
  629. .setIssueStatuses(request.paramAsStrings(PARAM_ISSUE_STATUSES))
  630. .setTags(request.paramAsStrings(PARAM_TAGS))
  631. .setTypes(allRuleTypesExceptHotspotsIfEmpty(request.paramAsStrings(PARAM_TYPES)))
  632. .setPciDss32(request.paramAsStrings(PARAM_PCI_DSS_32))
  633. .setPciDss40(request.paramAsStrings(PARAM_PCI_DSS_40))
  634. .setOwaspAsvsLevel(request.paramAsInt(PARAM_OWASP_ASVS_LEVEL))
  635. .setOwaspAsvs40(request.paramAsStrings(PARAM_OWASP_ASVS_40))
  636. .setOwaspTop10(request.paramAsStrings(PARAM_OWASP_TOP_10))
  637. .setOwaspTop10For2021(request.paramAsStrings(PARAM_OWASP_TOP_10_2021))
  638. .setSansTop25(request.paramAsStrings(PARAM_SANS_TOP_25))
  639. .setCwe(request.paramAsStrings(PARAM_CWE))
  640. .setSonarsourceSecurity(request.paramAsStrings(PARAM_SONARSOURCE_SECURITY))
  641. .setTimeZone(request.param(PARAM_TIMEZONE))
  642. .setCodeVariants(request.paramAsStrings(PARAM_CODE_VARIANTS))
  643. .setFixedInPullRequest(request.param(PARAM_FIXED_IN_PULL_REQUEST));
  644. }
  645. private void checkIfNeedIssueSync(DbSession dbSession, SearchRequest searchRequest) {
  646. List<String> components = searchRequest.getComponentKeys();
  647. if (components != null && !components.isEmpty()) {
  648. issueIndexSyncProgressChecker.checkIfAnyComponentsNeedIssueSync(dbSession, components);
  649. } else {
  650. // component keys not provided - asking for global
  651. issueIndexSyncProgressChecker.checkIfIssueSyncInProgress(dbSession);
  652. }
  653. }
  654. private static List<String> allRuleTypesExceptHotspotsIfEmpty(@Nullable List<String> types) {
  655. if (types == null || types.isEmpty()) {
  656. return ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS.stream().map(Enum::name).toList();
  657. }
  658. return types;
  659. }
  660. private List<String> getLogins(DbSession dbSession, @Nullable List<String> assigneeLogins) {
  661. List<String> userLogins = new ArrayList<>();
  662. for (String login : ofNullable(assigneeLogins).orElse(emptyList())) {
  663. if (LOGIN_MYSELF.equals(login)) {
  664. if (userSession.getLogin() == null) {
  665. userLogins.add(UNKNOWN);
  666. } else {
  667. userLogins.add(userSession.getLogin());
  668. }
  669. } else {
  670. userLogins.add(login);
  671. }
  672. }
  673. List<UserDto> userDtos = dbClient.userDao().selectByLogins(dbSession, userLogins);
  674. List<String> assigneeUuid = userDtos.stream().map(UserDto::getUuid).toList();
  675. if ((assigneeLogins != null) && firstNonNull(assigneeUuid, emptyList()).isEmpty()) {
  676. assigneeUuid = List.of("non-existent-uuid");
  677. }
  678. return assigneeUuid;
  679. }
  680. }