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.

Sidebar.tsx 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 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. import * as React from 'react';
  21. import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
  22. import { isBranch, isPullRequest } from '../../../helpers/branch-like';
  23. import { AppState } from '../../../types/appstate';
  24. import { BranchLike } from '../../../types/branch-like';
  25. import { ComponentQualifier, isApplication, isPortfolioLike } from '../../../types/component';
  26. import {
  27. Facet,
  28. ReferencedComponent,
  29. ReferencedLanguage,
  30. ReferencedRule
  31. } from '../../../types/issues';
  32. import { GlobalSettingKeys } from '../../../types/settings';
  33. import { Component, Dict, UserBase } from '../../../types/types';
  34. import { Query } from '../utils';
  35. import AssigneeFacet from './AssigneeFacet';
  36. import AuthorFacet from './AuthorFacet';
  37. import CreationDateFacet from './CreationDateFacet';
  38. import DirectoryFacet from './DirectoryFacet';
  39. import FileFacet from './FileFacet';
  40. import LanguageFacet from './LanguageFacet';
  41. import ProjectFacet from './ProjectFacet';
  42. import ResolutionFacet from './ResolutionFacet';
  43. import RuleFacet from './RuleFacet';
  44. import ScopeFacet from './ScopeFacet';
  45. import SeverityFacet from './SeverityFacet';
  46. import StandardFacet from './StandardFacet';
  47. import StatusFacet from './StatusFacet';
  48. import TagFacet from './TagFacet';
  49. import TypeFacet from './TypeFacet';
  50. export interface Props {
  51. appState: AppState;
  52. branchLike?: BranchLike;
  53. component: Component | undefined;
  54. createdAfterIncludesTime: boolean;
  55. facets: Dict<Facet | undefined>;
  56. loadSearchResultCount: (property: string, changes: Partial<Query>) => Promise<Facet>;
  57. loadingFacets: Dict<boolean>;
  58. myIssues: boolean;
  59. onFacetToggle: (property: string) => void;
  60. onFilterChange: (changes: Partial<Query>) => void;
  61. openFacets: Dict<boolean>;
  62. query: Query;
  63. referencedComponentsById: Dict<ReferencedComponent>;
  64. referencedComponentsByKey: Dict<ReferencedComponent>;
  65. referencedLanguages: Dict<ReferencedLanguage>;
  66. referencedRules: Dict<ReferencedRule>;
  67. referencedUsers: Dict<UserBase>;
  68. }
  69. export class Sidebar extends React.PureComponent<Props> {
  70. renderComponentFacets() {
  71. const { component, facets, loadingFacets, openFacets, query, branchLike } = this.props;
  72. const hasFileOrDirectory =
  73. !isApplication(component?.qualifier) && !isPortfolioLike(component?.qualifier);
  74. if (!component || !hasFileOrDirectory) {
  75. return null;
  76. }
  77. const commonProps = {
  78. componentKey: component.key,
  79. loadSearchResultCount: this.props.loadSearchResultCount,
  80. onChange: this.props.onFilterChange,
  81. onToggle: this.props.onFacetToggle,
  82. query
  83. };
  84. return (
  85. <>
  86. {component.qualifier !== ComponentQualifier.Directory && (
  87. <DirectoryFacet
  88. branchLike={branchLike}
  89. directories={query.directories}
  90. fetching={loadingFacets.directories === true}
  91. open={!!openFacets.directories}
  92. stats={facets.directories}
  93. {...commonProps}
  94. />
  95. )}
  96. <FileFacet
  97. branchLike={branchLike}
  98. fetching={loadingFacets.files === true}
  99. files={query.files}
  100. open={!!openFacets.files}
  101. stats={facets.files}
  102. {...commonProps}
  103. />
  104. </>
  105. );
  106. }
  107. render() {
  108. const {
  109. appState: { settings },
  110. component,
  111. createdAfterIncludesTime,
  112. facets,
  113. openFacets,
  114. query,
  115. branchLike
  116. } = this.props;
  117. const disableDeveloperAggregatedInfo =
  118. settings[GlobalSettingKeys.DeveloperAggregatedInfoDisabled] === 'true';
  119. const branch =
  120. (isBranch(branchLike) && branchLike.name) ||
  121. (isPullRequest(branchLike) && branchLike.branch) ||
  122. undefined;
  123. const displayProjectsFacet =
  124. !component || !['TRK', 'BRC', 'DIR', 'DEV_PRJ'].includes(component.qualifier);
  125. const displayAuthorFacet = !component || component.qualifier !== 'DEV';
  126. return (
  127. <>
  128. <TypeFacet
  129. fetching={this.props.loadingFacets.types === true}
  130. onChange={this.props.onFilterChange}
  131. onToggle={this.props.onFacetToggle}
  132. open={!!openFacets.types}
  133. stats={facets.types}
  134. types={query.types}
  135. />
  136. <SeverityFacet
  137. fetching={this.props.loadingFacets.severities === true}
  138. onChange={this.props.onFilterChange}
  139. onToggle={this.props.onFacetToggle}
  140. open={!!openFacets.severities}
  141. severities={query.severities}
  142. stats={facets.severities}
  143. />
  144. <ScopeFacet
  145. fetching={this.props.loadingFacets.scopes === true}
  146. onChange={this.props.onFilterChange}
  147. onToggle={this.props.onFacetToggle}
  148. open={!!openFacets.scopes}
  149. stats={facets.scopes}
  150. scopes={query.scopes}
  151. />
  152. <ResolutionFacet
  153. fetching={this.props.loadingFacets.resolutions === true}
  154. onChange={this.props.onFilterChange}
  155. onToggle={this.props.onFacetToggle}
  156. open={!!openFacets.resolutions}
  157. resolutions={query.resolutions}
  158. resolved={query.resolved}
  159. stats={facets.resolutions}
  160. />
  161. <StatusFacet
  162. fetching={this.props.loadingFacets.statuses === true}
  163. onChange={this.props.onFilterChange}
  164. onToggle={this.props.onFacetToggle}
  165. open={!!openFacets.statuses}
  166. stats={facets.statuses}
  167. statuses={query.statuses}
  168. />
  169. <StandardFacet
  170. cwe={query.cwe}
  171. cweOpen={!!openFacets.cwe}
  172. cweStats={facets.cwe}
  173. fetchingCwe={this.props.loadingFacets.cwe === true}
  174. fetchingOwaspTop10={this.props.loadingFacets.owaspTop10 === true}
  175. fetchingSansTop25={this.props.loadingFacets.sansTop25 === true}
  176. fetchingSonarSourceSecurity={this.props.loadingFacets.sonarsourceSecurity === true}
  177. loadSearchResultCount={this.props.loadSearchResultCount}
  178. onChange={this.props.onFilterChange}
  179. onToggle={this.props.onFacetToggle}
  180. open={!!openFacets.standards}
  181. owaspTop10={query.owaspTop10}
  182. owaspTop10Open={!!openFacets.owaspTop10}
  183. owaspTop10Stats={facets.owaspTop10}
  184. query={query}
  185. sansTop25={query.sansTop25}
  186. sansTop25Open={!!openFacets.sansTop25}
  187. sansTop25Stats={facets.sansTop25}
  188. sonarsourceSecurity={query.sonarsourceSecurity}
  189. sonarsourceSecurityOpen={!!openFacets.sonarsourceSecurity}
  190. sonarsourceSecurityStats={facets.sonarsourceSecurity}
  191. />
  192. <CreationDateFacet
  193. component={component}
  194. createdAfter={query.createdAfter}
  195. createdAfterIncludesTime={createdAfterIncludesTime}
  196. createdAt={query.createdAt}
  197. createdBefore={query.createdBefore}
  198. createdInLast={query.createdInLast}
  199. fetching={this.props.loadingFacets.createdAt === true}
  200. onChange={this.props.onFilterChange}
  201. onToggle={this.props.onFacetToggle}
  202. open={!!openFacets.createdAt}
  203. sinceLeakPeriod={query.sinceLeakPeriod}
  204. stats={facets.createdAt}
  205. />
  206. <LanguageFacet
  207. fetching={this.props.loadingFacets.languages === true}
  208. loadSearchResultCount={this.props.loadSearchResultCount}
  209. onChange={this.props.onFilterChange}
  210. onToggle={this.props.onFacetToggle}
  211. open={!!openFacets.languages}
  212. query={query}
  213. referencedLanguages={this.props.referencedLanguages}
  214. selectedLanguages={query.languages}
  215. stats={facets.languages}
  216. />
  217. <RuleFacet
  218. fetching={this.props.loadingFacets.rules === true}
  219. languages={query.languages}
  220. loadSearchResultCount={this.props.loadSearchResultCount}
  221. onChange={this.props.onFilterChange}
  222. onToggle={this.props.onFacetToggle}
  223. open={!!openFacets.rules}
  224. query={query}
  225. referencedRules={this.props.referencedRules}
  226. rules={query.rules}
  227. stats={facets.rules}
  228. />
  229. <TagFacet
  230. component={component}
  231. branch={branch}
  232. fetching={this.props.loadingFacets.tags === true}
  233. loadSearchResultCount={this.props.loadSearchResultCount}
  234. onChange={this.props.onFilterChange}
  235. onToggle={this.props.onFacetToggle}
  236. open={!!openFacets.tags}
  237. query={query}
  238. stats={facets.tags}
  239. tags={query.tags}
  240. />
  241. {displayProjectsFacet && (
  242. <ProjectFacet
  243. component={component}
  244. fetching={this.props.loadingFacets.projects === true}
  245. loadSearchResultCount={this.props.loadSearchResultCount}
  246. onChange={this.props.onFilterChange}
  247. onToggle={this.props.onFacetToggle}
  248. open={!!openFacets.projects}
  249. projects={query.projects}
  250. query={query}
  251. referencedComponents={this.props.referencedComponentsByKey}
  252. stats={facets.projects}
  253. />
  254. )}
  255. {this.renderComponentFacets()}
  256. {!this.props.myIssues && !disableDeveloperAggregatedInfo && (
  257. <AssigneeFacet
  258. assigned={query.assigned}
  259. assignees={query.assignees}
  260. fetching={this.props.loadingFacets.assignees === true}
  261. loadSearchResultCount={this.props.loadSearchResultCount}
  262. onChange={this.props.onFilterChange}
  263. onToggle={this.props.onFacetToggle}
  264. open={!!openFacets.assignees}
  265. query={query}
  266. referencedUsers={this.props.referencedUsers}
  267. stats={facets.assignees}
  268. />
  269. )}
  270. {displayAuthorFacet && !disableDeveloperAggregatedInfo && (
  271. <AuthorFacet
  272. author={query.author}
  273. component={component}
  274. fetching={this.props.loadingFacets.author === true}
  275. loadSearchResultCount={this.props.loadSearchResultCount}
  276. onChange={this.props.onFilterChange}
  277. onToggle={this.props.onFacetToggle}
  278. open={!!openFacets.author}
  279. query={query}
  280. stats={facets.author}
  281. />
  282. )}
  283. </>
  284. );
  285. }
  286. }
  287. export default withAppStateContext(Sidebar);